Skip to content

Commit

Permalink
BIGTOP-4287: Support Agent remotely installed by Server (apache#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinw66 authored Nov 28, 2024
1 parent fe9a320 commit 7d3c388
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 28 deletions.
6 changes: 3 additions & 3 deletions bigtop-manager-agent/src/main/resources/assembly/agent.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>agent</id>
<formats>
<format>dir</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<baseDirectory>agent</baseDirectory>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>bigtop-manager-agent</baseDirectory>
<fileSets>
<fileSet>
<directory>${basedir}/src/main/resources</directory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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.HostPathReq;
import org.apache.bigtop.manager.server.model.req.HostReq;
import org.apache.bigtop.manager.server.model.vo.HostVO;
import org.apache.bigtop.manager.server.model.vo.PageVO;
Expand Down Expand Up @@ -103,4 +104,10 @@ public ResponseEntity<Boolean> checkConnection(@RequestBody @Validated HostReq h
HostDTO hostDTO = HostConverter.INSTANCE.fromReq2DTO(hostReq);
return ResponseEntity.success(hostService.checkConnection(hostDTO));
}

@Operation(summary = "Install dependencies", description = "Install dependencies on a host")
@PostMapping("/install-dependencies")
public ResponseEntity<Boolean> checkConnection(@RequestBody @Validated HostPathReq hostPathReq) {
return ResponseEntity.success(hostService.installDependencies(hostPathReq.getHostIds(), hostPathReq.getPath()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ 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),
HOST_UNABLE_TO_EXEC_COMMAND(12004, LocaleKeys.HOST_UNABLE_TO_EXEC_COMMAND),
HOST_HAS_COMPONENTS(12005, LocaleKeys.HOST_HAS_COMPONENTS),

// Stack Exceptions -- 13000 ~ 13999
STACK_NOT_FOUND(13000, LocaleKeys.STACK_NOT_FOUND),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_UNABLE_TO_EXEC_COMMAND("host.unable.to.exec.command"),
HOST_HAS_COMPONENTS("host.has.components"),

STACK_NOT_FOUND("stack.not.found"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.model.req;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.List;

@Data
public class HostPathReq {

@Schema(example = "[1, 2]")
private List<Long> hostIds;

@Schema(example = "/opt")
private String path;
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,13 @@ public interface HostService {
* @return true if all hosts are able to connect
*/
Boolean checkConnection(HostDTO hostDTO);

/**
* Install dependencies
*
* @param hostIds host ids
* @param path remote host path
* @return true if all dependencies are installed
*/
Boolean installDependencies(List<Long> hostIds, String path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
*/
package org.apache.bigtop.manager.server.service.impl;

import org.apache.bigtop.manager.common.constants.MessageConstants;
import org.apache.bigtop.manager.common.shell.ShellResult;
import org.apache.bigtop.manager.dao.po.HostPO;
import org.apache.bigtop.manager.dao.po.RepoPO;
import org.apache.bigtop.manager.dao.query.HostQuery;
import org.apache.bigtop.manager.dao.repository.ComponentDao;
import org.apache.bigtop.manager.dao.repository.HostDao;
import org.apache.bigtop.manager.dao.repository.RepoDao;
import org.apache.bigtop.manager.server.enums.ApiExceptionEnum;
import org.apache.bigtop.manager.server.enums.HealthyStatusEnum;
import org.apache.bigtop.manager.server.enums.HostAuthTypeEnum;
Expand Down Expand Up @@ -53,6 +56,9 @@
@Service
public class HostServiceImpl implements HostService {

@Resource
private RepoDao repoDao;

@Resource
private HostDao hostDao;

Expand Down Expand Up @@ -140,25 +146,9 @@ public Boolean delete(Long id) {
@Override
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);
}

ShellResult result = execCommandOnRemoteHost(hostDTO, hostname, 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);
Expand All @@ -173,4 +163,96 @@ public Boolean checkConnection(HostDTO hostDTO) {

return true;
}

@Override
public Boolean installDependencies(List<Long> hostIds, String path) {
List<RepoPO> repoPOList = repoDao.findAll();
Map<String, RepoPO> archRepoMap = repoPOList.stream()
.filter(repoPO -> repoPO.getType() == 2)
.collect(Collectors.toMap(RepoPO::getArch, repo -> repo));

List<HostPO> hostPOList = hostDao.findByIds(hostIds);
for (HostPO hostPO : hostPOList) {
HostDTO hostDTO = HostConverter.INSTANCE.fromPO2DTO(hostPO);

// Get host arch
String arch = execCommandOnRemoteHost(hostDTO, hostDTO.getHostname(), "arch")
.getOutput()
.trim();
arch = arch.equals("arm64") ? "aarch64" : arch;

// Download & Extract agent tarball
String repoUrl = archRepoMap.get(arch).getBaseUrl();
String tarballUrl = repoUrl + "/bigtop-manager-agent.tar.gz";
String command = "curl -L " + tarballUrl + " | tar -xz -C " + path;
ShellResult result = execCommandOnRemoteHost(hostDTO, hostDTO.getHostname(), command);
if (result.getExitCode() != MessageConstants.SUCCESS_CODE) {
hostPO.setErrInfo(result.getErrMsg());
hostDao.updateById(hostPO);

log.error(
"Unable to download & extract agent tarball, hostname: {}, msg: {}",
hostDTO.getHostname(),
result.getErrMsg());
throw new ApiException(ApiExceptionEnum.HOST_UNABLE_TO_EXEC_COMMAND, hostDTO.getHostname());
}

// Run agent in background
command = "nohup " + path + "/bigtop-manager-agent/bin/start.sh > /dev/null 2>&1 &";
result = execCommandOnRemoteHost(hostDTO, hostDTO.getHostname(), command);
if (result.getExitCode() != MessageConstants.SUCCESS_CODE) {
hostPO.setErrInfo(result.getErrMsg());
hostDao.updateById(hostPO);

log.error("Unable to start agent, hostname: {}, msg: {}", hostDTO.getHostname(), result.getErrMsg());
throw new ApiException(ApiExceptionEnum.HOST_UNABLE_TO_EXEC_COMMAND, hostDTO.getHostname());
}

// Check the process, the agent may encounter some errors and exit when starting
// So we need to wait for a while before the check
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
log.error("Thread sleep interrupted", e);
}
command = "ps -ef | grep bigtop-manager-agent | grep -v grep";
result = execCommandOnRemoteHost(hostDTO, hostDTO.getHostname(), command);
if (result.getExitCode() != MessageConstants.SUCCESS_CODE
|| !result.getOutput().contains("bigtop-manager-agent")) {
hostPO.setErrInfo("Unable to start agent process, please check the log");
hostDao.updateById(hostPO);

log.error("Unable to start agent process, hostname: {}", hostDTO.getHostname());
throw new ApiException(ApiExceptionEnum.HOST_UNABLE_TO_EXEC_COMMAND, hostDTO.getHostname());
}

hostPO.setStatus(HealthyStatusEnum.HEALTHY.getCode());
hostDao.updateById(hostPO);
}

return true;
}

private ShellResult execCommandOnRemoteHost(HostDTO hostDTO, String hostname, String command) {
HostAuthTypeEnum authType = HostAuthTypeEnum.fromCode(hostDTO.getAuthType());
try {
return switch (authType) {
case PASSWORD -> RemoteSSHUtils.executeCommand(
hostname, hostDTO.getSshPort(), hostDTO.getSshUser(), hostDTO.getSshPassword(), command);
case KEY -> RemoteSSHUtils.executeCommand(
hostname,
hostDTO.getSshPort(),
hostDTO.getSshUser(),
hostDTO.getSshKeyFilename(),
hostDTO.getSshKeyString(),
hostDTO.getSshKeyPassword(),
command);
case NO_AUTH -> RemoteSSHUtils.executeCommand(
hostname, hostDTO.getSshPort(), hostDTO.getSshUser(), command);
};
} catch (Exception e) {
log.error("Unable to exec command on host, hostname: {}, command: {}", hostname, command, e);
throw new ApiException(ApiExceptionEnum.HOST_UNABLE_TO_EXEC_COMMAND, hostname);
}
}
}
6 changes: 3 additions & 3 deletions bigtop-manager-server/src/main/resources/assembly/server.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>server</id>
<formats>
<format>dir</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<baseDirectory>server</baseDirectory>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>bigtop-manager-server</baseDirectory>
<fileSets>
<fileSet>
<directory>${basedir}/src/main/resources</directory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ CREATE TABLE `host`
`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,
`err_info` TEXT DEFAULT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`create_by` BIGINT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ CREATE TABLE host
total_memory_size BIGINT,
"desc" VARCHAR(255) DEFAULT NULL,
status INT DEFAULT NULL,
err_info VARCHAR(255) DEFAULT NULL,
err_info TEXT DEFAULT NULL,
create_time TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP,
create_by BIGINT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.unable.to.exec.command=Unable to execute command on host [{0}]
host.has.components=Host still has components, please remove them first

stack.not.found=Stack not exist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ host.not.found=主机不存在
host.assigned=主机 [{0}] 已属于其他集群
host.not.connected=主机 [{0}] 未连接
host.unable.to.connect=无法连接到主机 [{0}]
host.unable.to.exec.command=无法在主机 [{0}] 上执行命令
host.has.components=主机上仍有组件,请先移除

stack.not.found=组件栈不存在
Expand Down
6 changes: 4 additions & 2 deletions dev-support/docker/containers/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ create_container() {
log "Create ${container_name}"
if [ $i -eq 1 ]; then
docker run -itd -p 15005:5005 -p 15006:5006 -p 18080:8080 --name ${container_name} --hostname ${container_name} --network bigtop-manager --cap-add=SYS_TIME bigtop-manager/develop:${OS}
docker cp ../../../bigtop-manager-server/target/bigtop-manager-server ${container_name}:/opt/
docker cp ../../../bigtop-manager-server/target/bigtop-manager-server.tar.gz ${container_name}:/opt/
docker exec ${container_name} bash -c "cd /opt && tar -zxvf bigtop-manager-server.tar.gz"
SERVER_PUB_KEY=`docker exec ${container_name} /bin/cat /root/.ssh/id_rsa.pub`
else
docker run -itd --name ${container_name} --hostname ${container_name} --network bigtop-manager --cap-add=SYS_TIME bigtop-manager/develop:${OS}
fi
docker cp ../../../bigtop-manager-agent/target/bigtop-manager-agent ${container_name}:/opt/
docker cp ../../../bigtop-manager-agent/target/bigtop-manager-agent.tar.gz ${container_name}:/opt/
docker exec ${container_name} bash -c "cd /opt && tar -zxvf bigtop-manager-agent.tar.gz"
docker exec ${container_name} bash -c "echo '$SERVER_PUB_KEY' > /root/.ssh/authorized_keys"
docker exec ${container_name} ssh-keygen -N '' -t rsa -b 2048 -f /etc/ssh/ssh_host_rsa_key
docker exec ${container_name} ssh-keygen -N '' -t ecdsa -b 256 -f /etc/ssh/ssh_host_ecdsa_key
Expand Down

0 comments on commit 7d3c388

Please sign in to comment.