This is an automated email from the ASF dual-hosted git repository.

wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git


The following commit(s) were added to refs/heads/main by this push:
     new bbbeae8  BIGTOP-4245: Refactor APIs for Host (#92)
bbbeae8 is described below

commit bbbeae8d5fca037120ca705cd33e8d4e91a0b461
Author: Zhiguo Wu <[email protected]>
AuthorDate: Tue Oct 22 22:24:10 2024 +0800

    BIGTOP-4245: Refactor APIs for Host (#92)
---
 .../agent/service/CommandServiceGrpcImpl.java      |   4 +-
 .../agent/service/TaskLogServiceGrpcImpl.java      |   4 +-
 .../bigtop/manager/agent/utils/LogFileUtils.java   |  44 --------
 bigtop-manager-bom/pom.xml                         |   8 ++
 .../manager/common/utils/ProjectPathUtils.java     |  68 +++++++++++
 .../org/apache/bigtop/manager/dao/po/HostPO.java   |  38 ++++++-
 .../apache/bigtop/manager/dao/query/HostQuery.java |  18 +--
 .../manager/dao/repository/HostComponentDao.java   |   2 +
 .../bigtop/manager/dao/repository/HostDao.java     |   5 +
 .../resources/mapper/mysql/HostComponentMapper.xml |   5 +
 .../src/main/resources/mapper/mysql/HostMapper.xml |  46 +++++++-
 .../mapper/postgresql/HostComponentMapper.xml      |   5 +
 .../resources/mapper/postgresql/HostMapper.xml     |  54 +++++++--
 bigtop-manager-server/pom.xml                      |  25 +++--
 .../manager/server/controller/FileController.java  |  77 +++++++++++++
 .../manager/server/controller/HostController.java  |  35 +++++-
 .../manager/server/enums/ApiExceptionEnum.java     |   4 +
 .../vo/HostVO.java => enums/HostAuthTypeEnum.java} |  46 ++++----
 .../vo/HostVO.java => enums/HostStatus.java}       |  46 ++++----
 .../bigtop/manager/server/enums/LocaleKeys.java    |   3 +-
 .../server/model/converter/HostConverter.java      |  15 ++-
 .../bigtop/manager/server/model/dto/HostDTO.java   |  21 +++-
 .../bigtop/manager/server/model/req/HostReq.java   |  36 ++++--
 .../bigtop/manager/server/model/vo/HostVO.java     |  28 ++++-
 .../server/scheduler/HostInfoScheduler.java        |   6 +-
 .../bigtop/manager/server/service/HostService.java |  12 +-
 .../server/service/impl/HostServiceImpl.java       |  96 ++++++++++++----
 .../manager/server/utils/RemoteSSHUtils.java       | 125 +++++++++++++++++++++
 .../src/main/resources/ddl/MySQL-DDL-CREATE.sql    |  20 +++-
 .../main/resources/ddl/PostgreSQL-DDL-CREATE.sql   |  25 +++--
 .../main/resources/i18n/messages_en_US.properties  |   3 +
 .../main/resources/i18n/messages_zh_CN.properties  |   3 +
 .../src/main/resources/logback-spring.xml          |   2 +-
 .../server/controller/HostControllerTest.java      |  38 +++----
 34 files changed, 750 insertions(+), 217 deletions(-)

diff --git 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/CommandServiceGrpcImpl.java
 
b/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/CommandServiceGrpcImpl.java
index c9d9368..920e418 100644
--- 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/CommandServiceGrpcImpl.java
+++ 
b/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/CommandServiceGrpcImpl.java
@@ -21,7 +21,7 @@ package org.apache.bigtop.manager.agent.service;
 import org.apache.bigtop.manager.agent.cache.Caches;
 import org.apache.bigtop.manager.agent.executor.CommandExecutor;
 import org.apache.bigtop.manager.agent.executor.CommandExecutors;
-import org.apache.bigtop.manager.agent.utils.LogFileUtils;
+import org.apache.bigtop.manager.common.utils.ProjectPathUtils;
 import org.apache.bigtop.manager.grpc.generated.CommandReply;
 import org.apache.bigtop.manager.grpc.generated.CommandRequest;
 import org.apache.bigtop.manager.grpc.generated.CommandServiceGrpc;
@@ -64,7 +64,7 @@ public class CommandServiceGrpcImpl extends 
CommandServiceGrpc.CommandServiceImp
     }
 
     private void truncateLogFile(Long taskId) {
-        String filePath = LogFileUtils.getLogFilePath(taskId);
+        String filePath = ProjectPathUtils.getLogFilePath(taskId);
         File file = new File(filePath);
         if (file.exists()) {
             try (RandomAccessFile rf = new RandomAccessFile(file, "rw")) {
diff --git 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/TaskLogServiceGrpcImpl.java
 
b/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/TaskLogServiceGrpcImpl.java
index 18d7269..c00acb4 100644
--- 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/TaskLogServiceGrpcImpl.java
+++ 
b/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/service/TaskLogServiceGrpcImpl.java
@@ -19,7 +19,7 @@
 package org.apache.bigtop.manager.agent.service;
 
 import org.apache.bigtop.manager.agent.cache.Caches;
-import org.apache.bigtop.manager.agent.utils.LogFileUtils;
+import org.apache.bigtop.manager.common.utils.ProjectPathUtils;
 import org.apache.bigtop.manager.grpc.generated.TaskLogReply;
 import org.apache.bigtop.manager.grpc.generated.TaskLogRequest;
 import org.apache.bigtop.manager.grpc.generated.TaskLogServiceGrpc;
@@ -38,7 +38,7 @@ public class TaskLogServiceGrpcImpl extends 
TaskLogServiceGrpc.TaskLogServiceImp
 
     @Override
     public void getLog(TaskLogRequest request, StreamObserver<TaskLogReply> 
responseObserver) {
-        String path = LogFileUtils.getLogFilePath(request.getTaskId());
+        String path = ProjectPathUtils.getLogFilePath(request.getTaskId());
         try (RandomAccessFile file = new RandomAccessFile(path, "r")) {
             // Read from beginning
             long fileLength = file.length();
diff --git 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/utils/LogFileUtils.java
 
b/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/utils/LogFileUtils.java
deleted file mode 100644
index de3f482..0000000
--- 
a/bigtop-manager-agent/src/main/java/org/apache/bigtop/manager/agent/utils/LogFileUtils.java
+++ /dev/null
@@ -1,44 +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
- *
- *    https://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.
- */
-package org.apache.bigtop.manager.agent.utils;
-
-import org.apache.bigtop.manager.common.utils.Environments;
-
-import org.apache.commons.lang3.SystemUtils;
-
-import java.io.File;
-
-public class LogFileUtils {
-
-    public static String getLogFilePath(Long taskId) {
-        String baseDir;
-        if (Environments.isDevMode()) {
-            baseDir = SystemUtils.getUserDir().getPath();
-        } else {
-            File file = new File(LogFileUtils.class
-                    .getProtectionDomain()
-                    .getCodeSource()
-                    .getLocation()
-                    .getPath());
-            baseDir = file.getParentFile().getParentFile().getPath();
-        }
-
-        return baseDir + File.separator + "tasklogs" + File.separator + 
"task-" + taskId + ".log";
-    }
-}
diff --git a/bigtop-manager-bom/pom.xml b/bigtop-manager-bom/pom.xml
index 7616aa2..004e40e 100644
--- a/bigtop-manager-bom/pom.xml
+++ b/bigtop-manager-bom/pom.xml
@@ -48,6 +48,7 @@
         <oshi-core.version>6.4.11</oshi-core.version>
         <micrometer.version>1.12.4</micrometer.version>
         <jdbc.dm.version>8.1.2.192</jdbc.dm.version>
+        <sshd.version>2.14.0</sshd.version>
         <langchain4j.version>0.33.0</langchain4j.version>
         <dashscope.version>2.16.3</dashscope.version>
         
<mybatis-spring-boot-starter.version>3.0.3</mybatis-spring-boot-starter.version>
@@ -179,6 +180,13 @@
                 <version>${micrometer.version}</version>
             </dependency>
 
+            <!-- sshd -->
+            <dependency>
+                <groupId>org.apache.sshd</groupId>
+                <artifactId>sshd-core</artifactId>
+                <version>${sshd.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>com.github.oshi</groupId>
                 <artifactId>oshi-core</artifactId>
diff --git 
a/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
 
b/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
new file mode 100644
index 0000000..b5d614f
--- /dev/null
+++ 
b/bigtop-manager-common/src/main/java/org/apache/bigtop/manager/common/utils/ProjectPathUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    https://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.
+ */
+package org.apache.bigtop.manager.common.utils;
+
+import org.apache.commons.lang3.SystemUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+@Slf4j
+public class ProjectPathUtils {
+
+    public static String getLogFilePath(Long taskId) {
+        return getProjectBaseDir() + File.separator + "tasklogs" + 
File.separator + "task-" + taskId + ".log";
+    }
+
+    public static String getKeyStorePath() {
+        return getProjectStoreDir() + File.separator + "keys";
+    }
+
+    private static String getProjectBaseDir() {
+        if (Environments.isDevMode()) {
+            return SystemUtils.getUserDir().getPath();
+        } else {
+            File file = new File(ProjectPathUtils.class
+                    .getProtectionDomain()
+                    .getCodeSource()
+                    .getLocation()
+                    .getPath());
+            return file.getParentFile().getParentFile().getPath();
+        }
+    }
+
+    private static String getProjectStoreDir() {
+        String path = SystemUtils.getUserHome().getPath() + File.separator + 
".bigtop-manager";
+        Path p = Paths.get(path);
+        if (!Files.exists(p)) {
+            try {
+                Files.createDirectories(p);
+            } catch (Exception e) {
+                log.error("Create directory failed: {}", path, e);
+                throw new RuntimeException("Create directory failed: " + path, 
e);
+            }
+        }
+
+        return path;
+    }
+}
diff --git 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/HostPO.java 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/HostPO.java
index 5b41317..9084955 100644
--- 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/HostPO.java
+++ 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/po/HostPO.java
@@ -39,6 +39,30 @@ public class HostPO extends BasePO implements Serializable {
     @Column(name = "hostname")
     private String hostname;
 
+    @Column(name = "ssh_user")
+    private String sshUser;
+
+    @Column(name = "ssh_port")
+    private Integer sshPort;
+
+    @Column(name = "auth_type")
+    private Integer authType;
+
+    @Column(name = "ssh_password")
+    private String sshPassword;
+
+    @Column(name = "ssh_key_string")
+    private String sshKeyString;
+
+    @Column(name = "ssh_key_filename")
+    private String sshKeyFilename;
+
+    @Column(name = "ssh_key_password")
+    private String sshKeyPassword;
+
+    @Column(name = "grpc_port")
+    private Integer grpcPort;
+
     @Column(name = "ipv4")
     private String ipv4;
 
@@ -66,13 +90,21 @@ public class HostPO extends BasePO implements Serializable {
     @Column(name = "total_disk")
     private Long totalDisk;
 
-    @Column(name = "state")
-    private String state;
+    @Column(name = "desc")
+    private String desc;
+
+    @Column(name = "status")
+    private Integer status;
+
+    @Column(name = "err_info")
+    private String errInfo;
 
     @Column(name = "cluster_id", nullable = false)
     private Long clusterId;
 
     @Transient
-    @Column(name = "cluster_name")
     private String clusterName;
+
+    @Transient
+    private Integer componentNum;
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostnamesReq.java
 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/query/HostQuery.java
similarity index 74%
rename from 
bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostnamesReq.java
rename to 
bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/query/HostQuery.java
index 487cd31..5e28083 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostnamesReq.java
+++ 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/query/HostQuery.java
@@ -16,18 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.bigtop.manager.server.model.req;
+package org.apache.bigtop.manager.dao.query;
 
-import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-import jakarta.validation.constraints.NotEmpty;
-import java.util.List;
-
 @Data
-public class HostnamesReq {
+public class HostQuery {
+
+    private String hostnameLike;
+
+    private Long clusterId;
+
+    private String ipv4Like;
 
-    @NotEmpty
-    @Schema(example = "[host1, host2]")
-    private List<String> hostnames;
+    private Integer status;
 }
diff --git 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostComponentDao.java
 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostComponentDao.java
index d298875..4df163e 100644
--- 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostComponentDao.java
+++ 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostComponentDao.java
@@ -48,4 +48,6 @@ public interface HostComponentDao extends 
BaseDao<HostComponentPO> {
 
     List<HostComponentPO> findAllByClusterIdAndServiceId(
             @Param("clusterId") Long clusterId, @Param("serviceId") Long 
serviceId);
+
+    Integer countByHostId(@Param("hostId") Long hostId);
 }
diff --git 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostDao.java
 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostDao.java
index be95624..59e4685 100644
--- 
a/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostDao.java
+++ 
b/bigtop-manager-dao/src/main/java/org/apache/bigtop/manager/dao/repository/HostDao.java
@@ -20,6 +20,7 @@
 package org.apache.bigtop.manager.dao.repository;
 
 import org.apache.bigtop.manager.dao.po.HostPO;
+import org.apache.bigtop.manager.dao.query.HostQuery;
 
 import org.apache.ibatis.annotations.Param;
 
@@ -35,4 +36,8 @@ public interface HostDao extends BaseDao<HostPO> {
     List<HostPO> findAllByClusterId(@Param("clusterId") Long clusterId);
 
     HostPO findByIdJoin(@Param("id") Long id);
+
+    List<HostPO> findByQuery(@Param("query") HostQuery query);
+
+    HostPO findDetailsById(@Param("id") Long id);
 }
diff --git 
a/bigtop-manager-dao/src/main/resources/mapper/mysql/HostComponentMapper.xml 
b/bigtop-manager-dao/src/main/resources/mapper/mysql/HostComponentMapper.xml
index 900397c..c01dd02 100644
--- a/bigtop-manager-dao/src/main/resources/mapper/mysql/HostComponentMapper.xml
+++ b/bigtop-manager-dao/src/main/resources/mapper/mysql/HostComponentMapper.xml
@@ -219,4 +219,9 @@
         on clu.stack_id = st.id
     </select>
 
+    <select id="countByHostId" resultType="java.lang.Integer">
+        select count(*)
+        from host_component
+        where host_id = #{hostId}
+    </select>
 </mapper>
\ No newline at end of file
diff --git a/bigtop-manager-dao/src/main/resources/mapper/mysql/HostMapper.xml 
b/bigtop-manager-dao/src/main/resources/mapper/mysql/HostMapper.xml
index ea168af..e530890 100644
--- a/bigtop-manager-dao/src/main/resources/mapper/mysql/HostMapper.xml
+++ b/bigtop-manager-dao/src/main/resources/mapper/mysql/HostMapper.xml
@@ -24,11 +24,16 @@
 <mapper namespace="org.apache.bigtop.manager.dao.repository.HostDao">
 
     <sql id="baseColumns">
-        id, hostname, ipv4, ipv6, os, arch, available_processors, 
free_memory_size, total_memory_size, free_disk, total_disk, state, cluster_id
+        id, hostname, ipv4, ipv6, os, arch, available_processors, 
free_memory_size, total_memory_size, free_disk, total_disk, status, cluster_id
     </sql>
 
     <sql id="baseColumnsV2">
-        ${alias}.id,  ${alias}.hostname,  ${alias}.ipv4,  ${alias}.ipv6,  
${alias}.os,  ${alias}.arch,  ${alias}.available_processors,  
${alias}.free_memory_size,  ${alias}.total_memory_size,  ${alias}.free_disk,  
${alias}.total_disk,  ${alias}.state,  ${alias}.cluster_id
+        ${alias}.id,  ${alias}.hostname,  ${alias}.ssh_user, 
${alias}.ssh_port, ${alias}.auth_type,
+        ${alias}.ssh_password, ${alias}.ssh_key_string, 
${alias}.ssh_key_filename, ${alias}.ssh_key_password,
+        ${alias}.grpc_port, ${alias}.ipv4, ${alias}.ipv6, ${alias}.os, 
${alias}.arch,
+        ${alias}.available_processors, ${alias}.free_memory_size, 
${alias}.total_memory_size,
+        ${alias}.free_disk, ${alias}.total_disk, ${alias}.desc, 
${alias}.status, ${alias}.err_info,
+        ${alias}.cluster_id
     </sql>
 
     <select id="findByHostname" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
@@ -100,4 +105,41 @@
         </where>
     </select>
 
+    <select id="findByQuery" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
+        select
+        <include refid="baseColumnsV2">
+            <property name="alias" value="h"/>
+        </include>, c.cluster_name, count(hc.id) as component_num
+        from host h
+        left join cluster c on h.cluster_id = c.id
+        left join host_component hc on h.id = hc.host_id
+        <where>
+            <if test="query.hostnameLike != null and query.hostnameLike != ''">
+                and h.hostname like concat('%', #{query.hostnameLike}, '%')
+            </if>
+            <if test="query.clusterId != null">
+                and h.cluster_id = #{query.clusterId}
+            </if>
+            <if test="query.ipv4Like != null and query.ipv4Like != ''">
+                and h.ipv4 like concat('%', #{query.ipv4Like}, '%')
+            </if>
+            <if test="query.status != null">
+                and h.status = #{query.status}
+            </if>
+        </where>
+        group by h.id
+    </select>
+
+    <select id="findDetailsById" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
+        select
+        <include refid="baseColumnsV2">
+            <property name="alias" value="h"/>
+        </include>, c.cluster_name, count(hc.id) as component_num
+        from host h
+        left join cluster c on h.cluster_id = c.id
+        left join host_component hc on h.id = hc.host_id
+        where h.id = #{id}
+        group by h.id
+        limit 1
+    </select>
 </mapper>
\ No newline at end of file
diff --git 
a/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostComponentMapper.xml
 
b/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostComponentMapper.xml
index 214dbc9..927a6e6 100644
--- 
a/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostComponentMapper.xml
+++ 
b/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostComponentMapper.xml
@@ -219,4 +219,9 @@
         on clu.stack_id = st.id
     </select>
 
+    <select id="countByHostId" resultType="java.lang.Integer">
+        select count(*)
+        from host_component
+        where host_id = #{hostId}
+    </select>
 </mapper>
\ No newline at end of file
diff --git 
a/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostMapper.xml 
b/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostMapper.xml
index ea168af..4659b42 100644
--- a/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostMapper.xml
+++ b/bigtop-manager-dao/src/main/resources/mapper/postgresql/HostMapper.xml
@@ -28,7 +28,12 @@
     </sql>
 
     <sql id="baseColumnsV2">
-        ${alias}.id,  ${alias}.hostname,  ${alias}.ipv4,  ${alias}.ipv6,  
${alias}.os,  ${alias}.arch,  ${alias}.available_processors,  
${alias}.free_memory_size,  ${alias}.total_memory_size,  ${alias}.free_disk,  
${alias}.total_disk,  ${alias}.state,  ${alias}.cluster_id
+        ${alias}.id,  ${alias}.hostname,  ${alias}.ssh_user, 
${alias}.ssh_port, ${alias}.auth_type,
+        ${alias}.ssh_password, ${alias}.ssh_key_string, 
${alias}.ssh_key_filename, ${alias}.ssh_key_password,
+        ${alias}.grpc_port, ${alias}.ipv4, ${alias}.ipv6, ${alias}.os, 
${alias}.arch,
+        ${alias}.available_processors, ${alias}.free_memory_size, 
${alias}.total_memory_size,
+        ${alias}.free_disk, ${alias}.total_disk, ${alias}."desc", 
${alias}.status, ${alias}.err_info,
+        ${alias}.cluster_id
     </sql>
 
     <select id="findByHostname" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
@@ -73,14 +78,7 @@
         ,clus.cluster_name
         from
         host h
-        inner join
-        (select * from cluster
-        <where>
-            <if test="clusterId != 0">
-                id = #{clusterId}
-            </if>
-        </where>
-        ) clus
+        left join cluster clus
         on h.cluster_id = clus.id
     </select>
 
@@ -100,4 +98,42 @@
         </where>
     </select>
 
+    <select id="findByQuery" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
+        select
+        <include refid="baseColumnsV2">
+            <property name="alias" value="h"/>
+        </include>, c.cluster_name, count(hc.id) as component_num
+        from host h
+        left join cluster c on h.cluster_id = c.id
+        left join host_component hc on h.id = hc.host_id
+        <where>
+            <if test="query.hostnameLike != null and query.hostnameLike != ''">
+                and h.hostname like concat('%', #{query.hostnameLike}, '%')
+            </if>
+            <if test="query.clusterId != null">
+                and h.cluster_id = #{query.clusterId}
+            </if>
+            <if test="query.ipv4Like != null and query.ipv4Like != ''">
+                and h.ipv4 like concat('%', #{query.ipv4Like}, '%')
+            </if>
+            <if test="query.status != null">
+                and h.status = #{query.status}
+            </if>
+        </where>
+        group by h.id
+    </select>
+
+    <select id="findDetailsById" 
resultType="org.apache.bigtop.manager.dao.po.HostPO">
+        select
+        <include refid="baseColumnsV2">
+            <property name="alias" value="h"/>
+        </include>, c.cluster_name, count(hc.id) as component_num
+        from host h
+        left join cluster c on h.cluster_id = c.id
+        left join host_component hc on h.id = hc.host_id
+        where h.id = #{id}
+        group by h.id
+        limit 1
+    </select>
+
 </mapper>
\ No newline at end of file
diff --git a/bigtop-manager-server/pom.xml b/bigtop-manager-server/pom.xml
index d6aecde..84d9bec 100644
--- a/bigtop-manager-server/pom.xml
+++ b/bigtop-manager-server/pom.xml
@@ -59,6 +59,14 @@
             <groupId>org.apache.bigtop</groupId>
             <artifactId>bigtop-manager-ui</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.bigtop</groupId>
+            <artifactId>bigtop-manager-ai-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.bigtop</groupId>
+            <artifactId>bigtop-manager-ai-assistant</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -80,6 +88,11 @@
             <artifactId>pagehelper-spring-boot-starter</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-client-spring-boot-starter</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.mysql</groupId>
             <artifactId>mysql-connector-j</artifactId>
@@ -144,16 +157,8 @@
         </dependency>
 
         <dependency>
-            <groupId>net.devh</groupId>
-            <artifactId>grpc-client-spring-boot-starter</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.bigtop</groupId>
-            <artifactId>bigtop-manager-ai-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.bigtop</groupId>
-            <artifactId>bigtop-manager-ai-assistant</artifactId>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
         </dependency>
     </dependencies>
 
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/FileController.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/FileController.java
new file mode 100644
index 0000000..ad27e5d
--- /dev/null
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/FileController.java
@@ -0,0 +1,77 @@
+/*
+ * 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
+ *
+ *    https://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.
+ */
+package org.apache.bigtop.manager.server.controller;
+
+import org.apache.bigtop.manager.common.utils.ProjectPathUtils;
+import org.apache.bigtop.manager.server.enums.ApiExceptionEnum;
+import org.apache.bigtop.manager.server.exception.ApiException;
+import org.apache.bigtop.manager.server.utils.ResponseEntity;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Slf4j
+@Tag(name = "File Controller")
+@RestController
+@RequestMapping("/files")
+public class FileController {
+
+    @Operation(summary = "upload key file", description = "Upload key file")
+    @PostMapping("/upload-key")
+    public ResponseEntity<String> uploadKeyFile(@RequestParam("file") 
MultipartFile file) {
+        try {
+            // Define the directory where the file will be saved
+            String uploadDir = ProjectPathUtils.getKeyStorePath();
+            Path uploadPath = Paths.get(uploadDir);
+
+            // Create the directory if it does not exist
+            if (!Files.exists(uploadPath)) {
+                Files.createDirectories(uploadPath);
+            }
+
+            // Generate a unique filename
+            LocalDateTime now = LocalDateTime.now();
+            DateTimeFormatter formatter = 
DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+            String filename = now.format(formatter) + ".key";
+
+            // Save the file to the desired location
+            Path filePath = uploadPath.resolve(filename);
+            Files.copy(file.getInputStream(), filePath, 
StandardCopyOption.REPLACE_EXISTING);
+
+            return ResponseEntity.success(filename);
+        } catch (Exception e) {
+            log.error("Upload key file failed: ", e);
+            throw new ApiException(ApiExceptionEnum.FILE_UPLOAD_FAILED);
+        }
+    }
+}
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/HostController.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/HostController.java
index 561c39a..3a67a66 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/HostController.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/controller/HostController.java
@@ -18,11 +18,12 @@
  */
 package org.apache.bigtop.manager.server.controller;
 
+import org.apache.bigtop.manager.dao.query.HostQuery;
 import org.apache.bigtop.manager.server.model.converter.HostConverter;
 import org.apache.bigtop.manager.server.model.dto.HostDTO;
 import org.apache.bigtop.manager.server.model.req.HostReq;
-import org.apache.bigtop.manager.server.model.req.HostnamesReq;
 import org.apache.bigtop.manager.server.model.vo.HostVO;
+import org.apache.bigtop.manager.server.model.vo.PageVO;
 import org.apache.bigtop.manager.server.service.HostService;
 import org.apache.bigtop.manager.server.utils.ResponseEntity;
 
@@ -37,6 +38,10 @@ import 
org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.tags.Tag;
 
 import jakarta.annotation.Resource;
@@ -51,9 +56,26 @@ public class HostController {
     private HostService hostService;
 
     @Operation(summary = "list", description = "List hosts")
+    @Parameters({
+        @Parameter(in = ParameterIn.QUERY, name = "pageNum", schema = 
@Schema(type = "integer", defaultValue = "1")),
+        @Parameter(in = ParameterIn.QUERY, name = "pageSize", schema = 
@Schema(type = "integer", defaultValue = "10")),
+        @Parameter(in = ParameterIn.QUERY, name = "orderBy", schema = 
@Schema(type = "string", defaultValue = "id")),
+        @Parameter(
+                in = ParameterIn.QUERY,
+                name = "sort",
+                description = "asc/desc",
+                schema = @Schema(type = "string", defaultValue = "asc"))
+    })
     @GetMapping
-    public ResponseEntity<List<HostVO>> list(@PathVariable Long clusterId) {
-        return ResponseEntity.success(hostService.list(clusterId));
+    public ResponseEntity<PageVO<HostVO>> list(@PathVariable Long clusterId, 
HostQuery hostQuery) {
+        return ResponseEntity.success(hostService.list(clusterId, hostQuery));
+    }
+
+    @Operation(summary = "add", description = "Add a host")
+    @PostMapping
+    public ResponseEntity<List<HostVO>> add(@PathVariable Long clusterId, 
@RequestBody @Validated HostReq hostReq) {
+        HostDTO hostDTO = HostConverter.INSTANCE.fromReq2DTO(hostReq);
+        return ResponseEntity.success(hostService.add(clusterId, hostDTO));
     }
 
     @Operation(summary = "get", description = "Get a host")
@@ -67,7 +89,7 @@ public class HostController {
     public ResponseEntity<HostVO> update(
             @PathVariable Long clusterId, @PathVariable Long id, @RequestBody 
@Validated HostReq hostReq) {
         HostDTO hostDTO = HostConverter.INSTANCE.fromReq2DTO(hostReq);
-        return ResponseEntity.success(hostService.update(id, hostDTO));
+        return ResponseEntity.success(hostService.update(id, clusterId, 
hostDTO));
     }
 
     @Operation(summary = "delete", description = "Delete a host")
@@ -79,7 +101,8 @@ public class HostController {
     @Operation(summary = "Check connection", description = "Check connection 
for hosts")
     @PostMapping("/check-connection")
     public ResponseEntity<Boolean> checkConnection(
-            @PathVariable Long clusterId, @RequestBody @Validated HostnamesReq 
hostnamesReq) {
-        return 
ResponseEntity.success(hostService.checkConnection(hostnamesReq.getHostnames()));
+            @PathVariable Long clusterId, @RequestBody @Validated HostReq 
hostReq) {
+        HostDTO hostDTO = HostConverter.INSTANCE.fromReq2DTO(hostReq);
+        return ResponseEntity.success(hostService.checkConnection(hostDTO));
     }
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/ApiExceptionEnum.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/ApiExceptionEnum.java
index 330f8c0..ce16778 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/ApiExceptionEnum.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/ApiExceptionEnum.java
@@ -39,6 +39,7 @@ public enum ApiExceptionEnum {
     HOST_ASSIGNED(12001, LocaleKeys.HOST_ASSIGNED),
     HOST_NOT_CONNECTED(12002, LocaleKeys.HOST_NOT_CONNECTED),
     HOST_UNABLE_TO_CONNECT(12003, LocaleKeys.HOST_UNABLE_TO_CONNECT),
+    HOST_HAS_COMPONENTS(12004, LocaleKeys.HOST_HAS_COMPONENTS),
 
     // Stack Exceptions -- 13000 ~ 13999
     STACK_NOT_FOUND(13000, LocaleKeys.STACK_NOT_FOUND),
@@ -68,6 +69,9 @@ public enum ApiExceptionEnum {
     CREDIT_INCORRECT(19003, LocaleKeys.CREDIT_INCORRECT),
     MODEL_NOT_SUPPORTED(19004, LocaleKeys.MODEL_NOT_SUPPORTED),
     CHAT_THREAD_NOT_FOUND(19005, LocaleKeys.CHAT_THREAD_NOT_FOUND),
+
+    // File Exceptions -- 30000 ~ 30999
+    FILE_UPLOAD_FAILED(30000, LocaleKeys.FILE_UPLOAD_FAILED),
     ;
 
     private final Integer code;
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostAuthTypeEnum.java
similarity index 58%
copy from 
bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
copy to 
bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostAuthTypeEnum.java
index c52ea3e..91496ff 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostAuthTypeEnum.java
@@ -16,36 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.bigtop.manager.server.model.vo;
+package org.apache.bigtop.manager.server.enums;
 
-import lombok.Data;
+import lombok.Getter;
 
-@Data
-public class HostVO {
+@Getter
+public enum HostAuthTypeEnum {
+    PASSWORD(1),
+    KEY(2),
+    NO_AUTH(3),
+    ;
 
-    private Long id;
+    private final Integer code;
 
-    private String clusterName;
+    HostAuthTypeEnum(Integer code) {
+        this.code = code;
+    }
 
-    private String hostname;
+    public static HostAuthTypeEnum fromCode(Integer code) {
+        for (HostAuthTypeEnum status : HostAuthTypeEnum.values()) {
+            if (status.code.equals(code)) {
+                return status;
+            }
+        }
 
-    private String ipv4;
-
-    private String ipv6;
-
-    private String arch;
-
-    private String os;
-
-    private Integer availableProcessors;
-
-    private Long freeMemorySize;
-
-    private Long totalMemorySize;
-
-    private Long freeDisk;
-
-    private Long totalDisk;
-
-    private String state;
+        throw new IllegalArgumentException("Invalid HostAuthTypeEnum code: " + 
code);
+    }
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostStatus.java
similarity index 62%
copy from 
bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
copy to 
bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostStatus.java
index c52ea3e..483a8e8 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/HostStatus.java
@@ -16,36 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.bigtop.manager.server.model.vo;
+package org.apache.bigtop.manager.server.enums;
 
-import lombok.Data;
+import lombok.Getter;
 
-@Data
-public class HostVO {
+@Getter
+public enum HostStatus {
+    HEALTHY(1),
+    UNHEALTHY(2),
+    UNKNOWN(3),
+    ;
 
-    private Long id;
+    private final Integer code;
 
-    private String clusterName;
+    HostStatus(Integer code) {
+        this.code = code;
+    }
 
-    private String hostname;
+    public static HostStatus fromCode(Integer code) {
+        for (HostStatus status : HostStatus.values()) {
+            if (status.code.equals(code)) {
+                return status;
+            }
+        }
 
-    private String ipv4;
-
-    private String ipv6;
-
-    private String arch;
-
-    private String os;
-
-    private Integer availableProcessors;
-
-    private Long freeMemorySize;
-
-    private Long totalMemorySize;
-
-    private Long freeDisk;
-
-    private Long totalDisk;
-
-    private String state;
+        return UNKNOWN;
+    }
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/LocaleKeys.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/LocaleKeys.java
index bbb6af1..87de8d5 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/LocaleKeys.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/enums/LocaleKeys.java
@@ -41,6 +41,7 @@ public enum LocaleKeys {
     HOST_ASSIGNED("host.assigned"),
     HOST_NOT_CONNECTED("host.not.connected"),
     HOST_UNABLE_TO_CONNECT("host.unable.to.connect"),
+    HOST_HAS_COMPONENTS("host.has.components"),
 
     STACK_NOT_FOUND("stack.not.found"),
 
@@ -64,7 +65,7 @@ public enum LocaleKeys {
     MODEL_NOT_SUPPORTED("model.not.supported"),
     CHAT_THREAD_NOT_FOUND("chat.thread.not.found"),
 
-    CHAT_LANGUAGE_PROMPT("chat.language.prompt"),
+    FILE_UPLOAD_FAILED("file.upload.failed"),
     ;
 
     private final String key;
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/converter/HostConverter.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/converter/HostConverter.java
index 66eeb12..cd1bccb 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/converter/HostConverter.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/converter/HostConverter.java
@@ -28,6 +28,7 @@ import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Mapper(config = MapStructSharedConfig.class)
 public interface HostConverter {
@@ -36,9 +37,19 @@ public interface HostConverter {
 
     HostDTO fromReq2DTO(HostReq hostReq);
 
-    HostPO fromDTO2PO(HostDTO hostDTO);
-
     HostVO fromPO2VO(HostPO hostPO);
 
     List<HostVO> fromPO2VO(List<HostPO> hostPOList);
+
+    HostPO fromDTO2POWithoutHostname(HostDTO hostDTO);
+
+    default List<HostPO> fromDTO2POList(HostDTO hostDTO) {
+        return hostDTO.getHostnames().stream()
+                .map(hostname -> {
+                    HostPO hostPO = fromDTO2POWithoutHostname(hostDTO);
+                    hostPO.setHostname(hostname);
+                    return hostPO;
+                })
+                .collect(Collectors.toList());
+    }
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/dto/HostDTO.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/dto/HostDTO.java
index 1119cb2..592dc18 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/dto/HostDTO.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/dto/HostDTO.java
@@ -20,10 +20,27 @@ package org.apache.bigtop.manager.server.model.dto;
 
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class HostDTO {
+    private List<String> hostnames;
+
+    private String sshUser;
+
+    private Integer sshPort;
+
+    private Integer authType;
+
+    private String sshPassword;
+
+    private String sshKeyString;
+
+    private String sshKeyFilename;
+
+    private String sshKeyPassword;
 
-    private Long clusterId;
+    private Integer grpcPort;
 
-    private String hostname;
+    private String desc;
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostReq.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostReq.java
index aad2035..9bf8b38 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostReq.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/req/HostReq.java
@@ -21,16 +21,38 @@ package org.apache.bigtop.manager.server.model.req;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
+import java.util.List;
 
 @Data
 public class HostReq {
 
-    @NotNull @Schema(example = "1")
-    private Long clusterId;
+    @Schema(example = "[host1, host2]")
+    private List<String> hostnames;
 
-    @NotEmpty
-    @Schema(example = "host1")
-    private String hostname;
+    @Schema(example = "root")
+    private String sshUser;
+
+    @Schema(example = "22")
+    private Integer sshPort;
+
+    @Schema(example = "1")
+    private Integer authType;
+
+    @Schema(example = "password")
+    private String sshPassword;
+
+    @Schema(example = "sshKeyString")
+    private String sshKeyString;
+
+    @Schema(example = "id_rsa123")
+    private String sshKeyFilename;
+
+    @Schema(example = "password")
+    private String sshKeyPassword;
+
+    @Schema(example = "8835")
+    private Integer grpcPort;
+
+    @Schema(example = "description")
+    private String desc;
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
index c52ea3e..e8545b4 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/model/vo/HostVO.java
@@ -25,10 +25,24 @@ public class HostVO {
 
     private Long id;
 
-    private String clusterName;
-
     private String hostname;
 
+    private String sshUser;
+
+    private Integer sshPort;
+
+    private Integer authType;
+
+    private String sshPassword;
+
+    private String sshKeyString;
+
+    private String sshKeyFilename;
+
+    private String sshKeyPassword;
+
+    private Integer grpcPort;
+
     private String ipv4;
 
     private String ipv6;
@@ -47,5 +61,13 @@ public class HostVO {
 
     private Long totalDisk;
 
-    private String state;
+    private String desc;
+
+    private Integer status;
+
+    private String errInfo;
+
+    private String clusterName;
+
+    private Integer componentNum;
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/scheduler/HostInfoScheduler.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/scheduler/HostInfoScheduler.java
index 33ebab0..5f646f1 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/scheduler/HostInfoScheduler.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/scheduler/HostInfoScheduler.java
@@ -18,7 +18,6 @@
  */
 package org.apache.bigtop.manager.server.scheduler;
 
-import org.apache.bigtop.manager.common.enums.MaintainState;
 import org.apache.bigtop.manager.dao.po.HostPO;
 import org.apache.bigtop.manager.dao.repository.HostDao;
 import org.apache.bigtop.manager.grpc.generated.HostInfoReply;
@@ -68,11 +67,8 @@ public class HostInfoScheduler {
             hostPO.setTotalMemorySize(reply.getTotalMemorySize());
             hostPO.setFreeDisk(reply.getFreeDisk());
             hostPO.setTotalDisk(reply.getTotalDisk());
-
-            hostPO.setState(MaintainState.STARTED.getName());
         } catch (Exception e) {
-            log.error("Error getting host info", e);
-            hostPO.setState(MaintainState.STOPPED.getName());
+            log.error("Error getting host info for {}", hostname, e);
         }
 
         hostDao.partialUpdateById(hostPO);
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/HostService.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/HostService.java
index d6206bb..f4e4a5e 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/HostService.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/HostService.java
@@ -18,8 +18,10 @@
  */
 package org.apache.bigtop.manager.server.service;
 
+import org.apache.bigtop.manager.dao.query.HostQuery;
 import org.apache.bigtop.manager.server.model.dto.HostDTO;
 import org.apache.bigtop.manager.server.model.vo.HostVO;
+import org.apache.bigtop.manager.server.model.vo.PageVO;
 
 import java.util.List;
 
@@ -30,7 +32,9 @@ public interface HostService {
      *
      * @return Hosts
      */
-    List<HostVO> list(Long clusterId);
+    PageVO<HostVO> list(Long clusterId, HostQuery hostQuery);
+
+    List<HostVO> add(Long clusterId, HostDTO hostDTO);
 
     /**
      * Save a host
@@ -51,7 +55,7 @@ public interface HostService {
      *
      * @return Host
      */
-    HostVO update(Long id, HostDTO hostDTO);
+    HostVO update(Long id, Long clusterId, HostDTO hostDTO);
 
     /**
      * Delete a host
@@ -63,8 +67,8 @@ public interface HostService {
     /**
      * Check hosts connection
      *
-     * @param hostnames hostname list
+     * @param hostDTO host infos
      * @return true if all hosts are able to connect
      */
-    Boolean checkConnection(List<String> hostnames);
+    Boolean checkConnection(HostDTO hostDTO);
 }
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/HostServiceImpl.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/HostServiceImpl.java
index 8bc79a3..daccb46 100644
--- 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/HostServiceImpl.java
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/service/impl/HostServiceImpl.java
@@ -18,21 +18,31 @@
  */
 package org.apache.bigtop.manager.server.service.impl;
 
-import org.apache.bigtop.manager.common.enums.MaintainState;
+import org.apache.bigtop.manager.common.shell.ShellResult;
 import org.apache.bigtop.manager.dao.po.HostPO;
+import org.apache.bigtop.manager.dao.query.HostQuery;
+import org.apache.bigtop.manager.dao.repository.HostComponentDao;
 import org.apache.bigtop.manager.dao.repository.HostDao;
 import org.apache.bigtop.manager.server.enums.ApiExceptionEnum;
+import org.apache.bigtop.manager.server.enums.HostAuthTypeEnum;
+import org.apache.bigtop.manager.server.enums.HostStatus;
 import org.apache.bigtop.manager.server.exception.ApiException;
-import org.apache.bigtop.manager.server.grpc.GrpcClient;
 import org.apache.bigtop.manager.server.model.converter.HostConverter;
 import org.apache.bigtop.manager.server.model.dto.HostDTO;
+import org.apache.bigtop.manager.server.model.query.PageQuery;
 import org.apache.bigtop.manager.server.model.vo.HostVO;
+import org.apache.bigtop.manager.server.model.vo.PageVO;
 import org.apache.bigtop.manager.server.service.HostService;
+import org.apache.bigtop.manager.server.utils.PageUtils;
+import org.apache.bigtop.manager.server.utils.RemoteSSHUtils;
 
 import org.apache.commons.collections4.CollectionUtils;
 
 import org.springframework.stereotype.Service;
 
+import com.github.pagehelper.Page;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import lombok.extern.slf4j.Slf4j;
 
 import jakarta.annotation.Resource;
@@ -48,19 +58,40 @@ public class HostServiceImpl implements HostService {
     @Resource
     private HostDao hostDao;
 
+    @Resource
+    private HostComponentDao hostComponentDao;
+
     @Override
-    public List<HostVO> list(Long clusterId) {
-        List<HostPO> hostPOList = hostDao.findAllByClusterId(clusterId);
-        if (CollectionUtils.isEmpty(hostPOList)) {
-            throw new ApiException(ApiExceptionEnum.HOST_NOT_FOUND);
+    public PageVO<HostVO> list(Long clusterId, HostQuery hostQuery) {
+        PageQuery pageQuery = PageUtils.getPageQuery();
+        try (Page<?> ignored =
+                PageHelper.startPage(pageQuery.getPageNum(), 
pageQuery.getPageSize(), pageQuery.getOrderBy())) {
+            List<HostPO> hostPOList = hostDao.findByQuery(hostQuery);
+            if (CollectionUtils.isEmpty(hostPOList)) {
+                throw new ApiException(ApiExceptionEnum.HOST_NOT_FOUND);
+            }
+
+            PageInfo<HostPO> pageInfo = new PageInfo<>(hostPOList);
+            return PageVO.of(pageInfo);
+        } finally {
+            PageHelper.clearPage();
         }
+    }
 
+    @Override
+    public List<HostVO> add(Long clusterId, HostDTO hostDTO) {
+        List<HostPO> hostPOList = 
HostConverter.INSTANCE.fromDTO2POList(hostDTO);
+        for (HostPO hostPO : hostPOList) {
+            hostPO.setClusterId(clusterId);
+            hostPO.setStatus(HostStatus.UNKNOWN.getCode());
+        }
+
+        hostDao.saveAll(hostPOList);
         return HostConverter.INSTANCE.fromPO2VO(hostPOList);
     }
 
     @Override
     public List<HostVO> batchSave(Long clusterId, List<String> hostnames) {
-
         List<HostPO> hostnameIn = hostDao.findAllByHostnameIn(hostnames);
         List<HostPO> hostPOList = new ArrayList<>();
 
@@ -71,7 +102,7 @@ public class HostServiceImpl implements HostService {
             HostPO hostPO = new HostPO();
             hostPO.setHostname(hostname);
             hostPO.setClusterId(clusterId);
-            hostPO.setState(MaintainState.INSTALLED.getName());
+            hostPO.setStatus(HostStatus.UNKNOWN.getCode());
 
             if (hostInMap.containsKey(hostname)) {
                 hostPO.setId(hostInMap.get(hostname).getId());
@@ -87,7 +118,7 @@ public class HostServiceImpl implements HostService {
 
     @Override
     public HostVO get(Long id) {
-        HostPO hostPO = hostDao.findByIdJoin(id);
+        HostPO hostPO = hostDao.findDetailsById(id);
         if (hostPO == null) {
             throw new ApiException(ApiExceptionEnum.HOST_NOT_FOUND);
         }
@@ -96,27 +127,54 @@ public class HostServiceImpl implements HostService {
     }
 
     @Override
-    public HostVO update(Long id, HostDTO hostDTO) {
-        HostPO hostPO = HostConverter.INSTANCE.fromDTO2PO(hostDTO);
+    public HostVO update(Long id, Long clusterId, HostDTO hostDTO) {
+        HostPO hostPO = 
HostConverter.INSTANCE.fromDTO2POWithoutHostname(hostDTO);
         hostPO.setId(id);
         hostDao.partialUpdateById(hostPO);
-
-        return HostConverter.INSTANCE.fromPO2VO(hostPO);
+        return get(id);
     }
 
     @Override
     public Boolean delete(Long id) {
+        if (hostComponentDao.countByHostId(id) > 0) {
+            throw new ApiException(ApiExceptionEnum.HOST_HAS_COMPONENTS);
+        }
+
         hostDao.deleteById(id);
         return true;
     }
 
     @Override
-    public Boolean checkConnection(List<String> hostnames) {
-        for (String hostname : hostnames) {
-            if (!GrpcClient.isChannelAlive(hostname)) {
-                // An api exception will throw if connection fails to 
establish, we don't need to handle the return
-                // value.
-                GrpcClient.createChannel(hostname);
+    public Boolean checkConnection(HostDTO hostDTO) {
+        String command = "hostname";
+        HostAuthTypeEnum authType = 
HostAuthTypeEnum.fromCode(hostDTO.getAuthType());
+        for (String hostname : hostDTO.getHostnames()) {
+            try {
+                ShellResult result = null;
+                switch (authType) {
+                    case PASSWORD -> result = RemoteSSHUtils.executeCommand(
+                            hostname, hostDTO.getSshPort(), 
hostDTO.getSshUser(), hostDTO.getSshPassword(), command);
+                    case KEY -> result = RemoteSSHUtils.executeCommand(
+                            hostname,
+                            hostDTO.getSshPort(),
+                            hostDTO.getSshUser(),
+                            hostDTO.getSshKeyFilename(),
+                            hostDTO.getSshKeyString(),
+                            hostDTO.getSshKeyPassword(),
+                            command);
+                    case NO_AUTH -> result = RemoteSSHUtils.executeCommand(
+                            hostname, hostDTO.getSshPort(), 
hostDTO.getSshUser(), command);
+                }
+
+                if (result.getExitCode() != 0) {
+                    log.error("Unable to connect to host, hostname: {}, msg: 
{}", hostname, result.getErrMsg());
+                    throw new 
ApiException(ApiExceptionEnum.HOST_UNABLE_TO_CONNECT, hostname);
+                } else {
+                    log.info("Successfully connected to host, hostname: {}, 
res: {}", hostname, result.getOutput());
+                }
+            } catch (Exception e) {
+                log.error("Unable to connect to host, hostname: {}", hostname, 
e);
+                throw new 
ApiException(ApiExceptionEnum.HOST_UNABLE_TO_CONNECT, hostname);
             }
         }
 
diff --git 
a/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/utils/RemoteSSHUtils.java
 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/utils/RemoteSSHUtils.java
new file mode 100644
index 0000000..53417f1
--- /dev/null
+++ 
b/bigtop-manager-server/src/main/java/org/apache/bigtop/manager/server/utils/RemoteSSHUtils.java
@@ -0,0 +1,125 @@
+/*
+ * 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
+ *
+ *    https://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.
+ */
+package org.apache.bigtop.manager.server.utils;
+
+import org.apache.bigtop.manager.common.shell.ShellResult;
+import org.apache.bigtop.manager.common.utils.ProjectPathUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.client.channel.ClientChannel;
+import org.apache.sshd.client.channel.ClientChannelEvent;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.concurrent.TimeUnit;
+
+public class RemoteSSHUtils {
+
+    public static ShellResult executeCommand(String host, Integer port, String 
user, String command) throws Exception {
+        try (SshClient client = getSshClient()) {
+            return run(client, host, port, user, command);
+        }
+    }
+
+    public static ShellResult executeCommand(String host, Integer port, String 
user, String password, String command)
+            throws Exception {
+        try (SshClient client = getSshClient(password)) {
+            return run(client, host, port, user, command);
+        }
+    }
+
+    public static ShellResult executeCommand(
+            String host,
+            Integer port,
+            String user,
+            String keyFilename,
+            String keyString,
+            String keyPassword,
+            String command)
+            throws Exception {
+        String keyPath = ProjectPathUtils.getKeyStorePath() + File.separator + 
keyFilename;
+        try (SshClient client = getSshClient(keyPath, keyString, keyPassword)) 
{
+            return run(client, host, port, user, command);
+        }
+    }
+
+    public static ShellResult run(SshClient client, String host, Integer port, 
String user, String command)
+            throws Exception {
+        client.start();
+        try (ClientSession session =
+                client.connect(user, host, port).verify(10, 
TimeUnit.SECONDS).getSession()) {
+            session.auth().verify(10, TimeUnit.SECONDS);
+            try (ByteArrayOutputStream responseStream = new 
ByteArrayOutputStream();
+                    ByteArrayOutputStream errStream = new 
ByteArrayOutputStream();
+                    ClientChannel channel = 
session.createExecChannel(command)) {
+                channel.setOut(responseStream);
+                channel.setErr(errStream);
+                channel.open().verify(10, TimeUnit.SECONDS);
+                channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);
+                String res = responseStream.toString();
+                String err = errStream.toString();
+                return new ShellResult(channel.getExitStatus(), res, err);
+            }
+        } finally {
+            client.stop();
+        }
+    }
+
+    public static SshClient getSshClient() {
+        return SshClient.setUpDefaultClient();
+    }
+
+    public static SshClient getSshClient(String password) {
+        SshClient client = SshClient.setUpDefaultClient();
+        
client.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(password));
+        return client;
+    }
+
+    public static SshClient getSshClient(String keyPath, String keyString, 
String keyPassword) throws Exception {
+        SshClient client = SshClient.setUpDefaultClient();
+        KeyPairResourceLoader loader = 
SecurityUtils.getKeyPairResourceParser();
+        FilePasswordProvider passwordProvider = null;
+        if (StringUtils.isNotBlank(keyPassword)) {
+            passwordProvider = FilePasswordProvider.of(keyPassword);
+        }
+
+        Collection<KeyPair> keys = null;
+        if (StringUtils.isNotBlank(keyPath)) {
+            keys = loader.loadKeyPairs(null, new File(keyPath).toPath(), 
passwordProvider);
+        } else {
+            InputStream keyStream = new 
ByteArrayInputStream(keyString.getBytes(StandardCharsets.UTF_8));
+            keys = loader.loadKeyPairs(null, () -> "keyStringResource", 
passwordProvider, keyStream);
+        }
+        client.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys));
+        return client;
+    }
+}
diff --git a/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql 
b/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
index 4928062..20d1bb0 100644
--- a/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
+++ b/bigtop-manager-server/src/main/resources/ddl/MySQL-DDL-CREATE.sql
@@ -132,21 +132,29 @@ CREATE TABLE `host`
     `id`                   BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
     `cluster_id`           BIGINT(20) UNSIGNED NOT NULL,
     `hostname`             VARCHAR(255) DEFAULT NULL,
+    `ssh_user`             VARCHAR(255) DEFAULT NULL,
+    `ssh_port`             INTEGER DEFAULT NULL,
+    `auth_type`            INTEGER DEFAULT NULL COMMENT '1-password, 2-key, 
3-no_auth',
+    `ssh_password`         VARCHAR(255) DEFAULT NULL,
+    `ssh_key_string`       TEXT DEFAULT NULL,
+    `ssh_key_filename`     VARCHAR(255) DEFAULT NULL,
+    `ssh_key_password`     VARCHAR(255) DEFAULT NULL,
+    `grpc_port`            INTEGER DEFAULT NULL,
     `ipv4`                 VARCHAR(32)  DEFAULT NULL,
     `ipv6`                 VARCHAR(32)  DEFAULT NULL,
     `arch`                 VARCHAR(32)  DEFAULT NULL,
     `os`                   VARCHAR(32)  DEFAULT NULL,
-    `processor_count`      INT          DEFAULT NULL,
-    `physical_memory`      BIGINT       DEFAULT NULL COMMENT 'Total Physical 
Memory(Bytes)',
-    `state`                VARCHAR(32)  DEFAULT NULL,
-    `create_time`          DATETIME     DEFAULT NULL,
-    `update_time`          DATETIME     DEFAULT NULL,
     `available_processors` INTEGER,
-    `create_by`            BIGINT,
     `free_disk`            BIGINT,
     `free_memory_size`     BIGINT,
     `total_disk`           BIGINT,
     `total_memory_size`    BIGINT,
+    `desc`                 VARCHAR(255) DEFAULT NULL,
+    `status`               INTEGER  DEFAULT NULL COMMENT '1-healthy, 
2-unhealthy, 3-unknown',
+    `err_info`             VARCHAR(255) DEFAULT NULL,
+    `create_time`          DATETIME     DEFAULT NULL,
+    `update_time`          DATETIME     DEFAULT NULL,
+    `create_by`            BIGINT,
     `update_by`            BIGINT,
     PRIMARY KEY (`id`),
     UNIQUE KEY `uk_hostname` (`hostname`, `cluster_id`),
diff --git 
a/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql 
b/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
index 206f269..ab05807 100644
--- a/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
+++ b/bigtop-manager-server/src/main/resources/ddl/PostgreSQL-DDL-CREATE.sql
@@ -128,27 +128,36 @@ CREATE TABLE host
     id                   BIGINT CHECK (id > 0)         NOT NULL GENERATED 
ALWAYS AS IDENTITY,
     cluster_id           BIGINT CHECK (cluster_id > 0) NOT NULL,
     hostname             VARCHAR(255) DEFAULT NULL,
+    ssh_user             VARCHAR(255) DEFAULT NULL,
+    ssh_port             INT DEFAULT NULL,
+    auth_type            INT DEFAULT NULL,
+    ssh_password         VARCHAR(255) DEFAULT NULL,
+    ssh_key_string       TEXT DEFAULT NULL,
+    ssh_key_filename     VARCHAR(255) DEFAULT NULL,
+    ssh_key_password     VARCHAR(255) DEFAULT NULL,
+    grpc_port            INT DEFAULT NULL,
     ipv4                 VARCHAR(32)  DEFAULT NULL,
     ipv6                 VARCHAR(32)  DEFAULT NULL,
     arch                 VARCHAR(32)  DEFAULT NULL,
     os                   VARCHAR(32)  DEFAULT NULL,
-    processor_count      INT          DEFAULT NULL,
-    physical_memory      BIGINT       DEFAULT NULL,
-    state                VARCHAR(32)  DEFAULT NULL,
-    create_time          TIMESTAMP(0) DEFAULT NULL,
-    update_time          TIMESTAMP(0) DEFAULT NULL,
-    available_processors INTEGER,
-    create_by            BIGINT,
+    available_processors INT,
     free_disk            BIGINT,
     free_memory_size     BIGINT,
     total_disk           BIGINT,
     total_memory_size    BIGINT,
+    "desc"               VARCHAR(255)  DEFAULT NULL,
+    status               INT  DEFAULT NULL,
+    err_info             VARCHAR(255)  DEFAULT NULL,
+    create_time          TIMESTAMP(0) DEFAULT NULL,
+    update_time          TIMESTAMP(0) DEFAULT NULL,
+    create_by            BIGINT,
     update_by            BIGINT,
     PRIMARY KEY (id),
     CONSTRAINT uk_hostname UNIQUE (hostname, cluster_id)
 );
 
-COMMENT ON COLUMN host.physical_memory IS 'Total Physical Memory(Bytes)';
+COMMENT ON COLUMN host.auth_type IS '1-password, 2-key, 3-no_auth';
+COMMENT ON COLUMN host.status IS '1-healthy, 2-unhealthy, 3-unknown';
 
 DROP INDEX IF EXISTS idx_host_cluster_id;
 CREATE INDEX idx_host_cluster_id ON host (cluster_id);
diff --git 
a/bigtop-manager-server/src/main/resources/i18n/messages_en_US.properties 
b/bigtop-manager-server/src/main/resources/i18n/messages_en_US.properties
index 051df6d..b52003b 100644
--- a/bigtop-manager-server/src/main/resources/i18n/messages_en_US.properties
+++ b/bigtop-manager-server/src/main/resources/i18n/messages_en_US.properties
@@ -35,6 +35,7 @@ host.not.found=Host not exist
 host.assigned=Hosts [{0}] already assigned to another cluster
 host.not.connected=Hosts [{0}] not connected
 host.unable.to.connect=Unable to connect to host [{0}]
+host.has.components=Host still has components, please remove them first
 
 stack.not.found=Stack not exist
 
@@ -57,3 +58,5 @@ permission.denied=permission denied
 credit.incorrect=credit incorrect
 model.not.supported=model not supported
 chat.thread.not.found=chat thread not found
+
+file.upload.failed=File upload failed, please check server log
diff --git 
a/bigtop-manager-server/src/main/resources/i18n/messages_zh_CN.properties 
b/bigtop-manager-server/src/main/resources/i18n/messages_zh_CN.properties
index e4c647f..d7f6343 100644
--- a/bigtop-manager-server/src/main/resources/i18n/messages_zh_CN.properties
+++ b/bigtop-manager-server/src/main/resources/i18n/messages_zh_CN.properties
@@ -35,6 +35,7 @@ host.not.found=主机不存在
 host.assigned=主机 [{0}] 已属于其他集群
 host.not.connected=主机 [{0}] 未连接
 host.unable.to.connect=无法连接到主机 [{0}]
+host.has.components=主机上仍有组件,请先移除
 
 stack.not.found=组件栈不存在
 
@@ -57,3 +58,5 @@ permission.denied=权限被拒绝
 credit.incorrect=凭证不正确
 model.not.supported=模型不支持
 chat.thread.not.found=线程不存在
+
+file.upload.failed=文件上传失败,请查看服务器日志
diff --git a/bigtop-manager-server/src/main/resources/logback-spring.xml 
b/bigtop-manager-server/src/main/resources/logback-spring.xml
index 0da8ba3..7ff015c 100644
--- a/bigtop-manager-server/src/main/resources/logback-spring.xml
+++ b/bigtop-manager-server/src/main/resources/logback-spring.xml
@@ -44,7 +44,7 @@
         </encoder>
     </appender>
 
-    <root level="DEBUG">
+    <root level="INFO">
         <appender-ref ref="STDOUT" />
         <appender-ref ref="SERVER_LOG_FILE" />
     </root>
diff --git 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/controller/HostControllerTest.java
 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/controller/HostControllerTest.java
index f7b6c2e..b11cba8 100644
--- 
a/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/controller/HostControllerTest.java
+++ 
b/bigtop-manager-server/src/test/java/org/apache/bigtop/manager/server/controller/HostControllerTest.java
@@ -18,10 +18,11 @@
  */
 package org.apache.bigtop.manager.server.controller;
 
+import org.apache.bigtop.manager.dao.query.HostQuery;
 import org.apache.bigtop.manager.server.model.dto.HostDTO;
 import org.apache.bigtop.manager.server.model.req.HostReq;
-import org.apache.bigtop.manager.server.model.req.HostnamesReq;
 import org.apache.bigtop.manager.server.model.vo.HostVO;
+import org.apache.bigtop.manager.server.model.vo.PageVO;
 import org.apache.bigtop.manager.server.service.HostService;
 import org.apache.bigtop.manager.server.utils.MessageSourceUtils;
 import org.apache.bigtop.manager.server.utils.ResponseEntity;
@@ -36,9 +37,6 @@ import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import java.util.Arrays;
-import java.util.List;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -72,10 +70,11 @@ class HostControllerTest {
     @Test
     void listReturnsAllHosts() {
         Long clusterId = 1L;
-        List<HostVO> hosts = Arrays.asList(new HostVO(), new HostVO());
-        when(hostService.list(clusterId)).thenReturn(hosts);
+        PageVO<HostVO> hosts = new PageVO<>();
+        HostQuery hostQuery = new HostQuery();
+        when(hostService.list(clusterId, hostQuery)).thenReturn(hosts);
 
-        ResponseEntity<List<HostVO>> response = hostController.list(clusterId);
+        ResponseEntity<PageVO<HostVO>> response = 
hostController.list(clusterId, hostQuery);
 
         assertTrue(response.isSuccess());
         assertEquals(hosts, response.getData());
@@ -100,7 +99,7 @@ class HostControllerTest {
         Long hostId = 1L;
         HostReq hostReq = new HostReq();
         HostVO updatedHost = new HostVO();
-        when(hostService.update(anyLong(), 
any(HostDTO.class))).thenReturn(updatedHost);
+        when(hostService.update(anyLong(), anyLong(), 
any(HostDTO.class))).thenReturn(updatedHost);
 
         ResponseEntity<HostVO> response = hostController.update(clusterId, 
hostId, hostReq);
 
@@ -123,11 +122,10 @@ class HostControllerTest {
     @Test
     void checkConnectionReturnsSuccess() {
         Long clusterId = 1L;
-        HostnamesReq hostnamesReq = new HostnamesReq();
-        hostnamesReq.setHostnames(Arrays.asList("host1", "host2"));
-        
when(hostService.checkConnection(hostnamesReq.getHostnames())).thenReturn(true);
+        HostReq hostReq = new HostReq();
+        when(hostService.checkConnection(any(HostDTO.class))).thenReturn(true);
 
-        ResponseEntity<Boolean> response = 
hostController.checkConnection(clusterId, hostnamesReq);
+        ResponseEntity<Boolean> response = 
hostController.checkConnection(clusterId, hostReq);
 
         assertTrue(response.isSuccess());
         assertTrue(response.getData());
@@ -136,12 +134,13 @@ class HostControllerTest {
     @Test
     void listReturnsEmptyForInvalidClusterId() {
         Long clusterId = 999L;
-        when(hostService.list(clusterId)).thenReturn(List.of());
+        HostQuery hostQuery = new HostQuery();
+        when(hostService.list(clusterId, hostQuery)).thenReturn(new 
PageVO<>());
 
-        ResponseEntity<List<HostVO>> response = hostController.list(clusterId);
+        ResponseEntity<PageVO<HostVO>> response = 
hostController.list(clusterId, hostQuery);
 
         assertTrue(response.isSuccess());
-        assertTrue(response.getData().isEmpty());
+        assertNull(response.getData().getContent());
     }
 
     @Test
@@ -161,7 +160,7 @@ class HostControllerTest {
         Long clusterId = 1L;
         Long hostId = 999L;
         HostReq hostReq = new HostReq();
-        when(hostService.update(anyLong(), 
any(HostDTO.class))).thenReturn(null);
+        when(hostService.update(anyLong(), anyLong(), 
any(HostDTO.class))).thenReturn(null);
 
         ResponseEntity<HostVO> response = hostController.update(clusterId, 
hostId, hostReq);
 
@@ -184,11 +183,10 @@ class HostControllerTest {
     @Test
     void checkConnectionReturnsFalseForInvalidHostnames() {
         Long clusterId = 1L;
-        HostnamesReq hostnamesReq = new HostnamesReq();
-        hostnamesReq.setHostnames(Arrays.asList("invalidHost1", 
"invalidHost2"));
-        
when(hostService.checkConnection(hostnamesReq.getHostnames())).thenReturn(false);
+        HostReq hostReq = new HostReq();
+        
when(hostService.checkConnection(any(HostDTO.class))).thenReturn(false);
 
-        ResponseEntity<Boolean> response = 
hostController.checkConnection(clusterId, hostnamesReq);
+        ResponseEntity<Boolean> response = 
hostController.checkConnection(clusterId, hostReq);
 
         assertTrue(response.isSuccess());
         assertFalse(response.getData());

Reply via email to