You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.8 KiB
188 lines
5.8 KiB
package net.educoder.service;
|
|
|
|
import cn.hutool.core.util.RandomUtil;
|
|
import com.google.common.cache.Cache;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import net.educoder.constant.CommonConstants;
|
|
import net.educoder.model.dto.CreateCloudHostDto;
|
|
import net.educoder.model.param.ResetCloudHostParam;
|
|
import net.educoder.util.JCloudUtil;
|
|
import net.educoder.util.ShellUtil;
|
|
import net.educoder.util.SleepUtil;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.ThreadUtils;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
|
|
/**
|
|
* @Author: youys
|
|
* @Date: 2022/8/18
|
|
* @Description:
|
|
*/
|
|
@Slf4j
|
|
@Service
|
|
public class CloudHostService {
|
|
|
|
@Value("${jcloud.username}")
|
|
private String username;
|
|
|
|
@Value("${jcloud.password}")
|
|
private String password;
|
|
|
|
@Value("${jcloud.imageId}")
|
|
private String imageId;
|
|
|
|
@Value("${jcloud.frpc.template}")
|
|
private String frpcTemplate;
|
|
|
|
@Value("${jcloud.frpc.host}")
|
|
private String frpcHost;
|
|
|
|
@Value("${jcloud.networkIds}")
|
|
private String networkIds;
|
|
|
|
@Autowired
|
|
@Qualifier("guavaCache")
|
|
private Cache<String, String> guavaCache;
|
|
|
|
|
|
private Object lock = new Object();
|
|
private Object networkLock = new Object();
|
|
|
|
private int index = 0;
|
|
|
|
private List<String> networkIdList;
|
|
|
|
/**
|
|
* 创建云主机
|
|
* 1、创建云主机
|
|
* 2、创建浮动ip
|
|
* 3、绑定浮动ip
|
|
*
|
|
* @param port 穿透的端口
|
|
* @return
|
|
*/
|
|
public CreateCloudHostDto oneStepCreateCloudHost(int port) {
|
|
String apiToken = guavaCache.getIfPresent(CommonConstants.API_TOKEN);
|
|
if (StringUtils.isBlank(apiToken)) {
|
|
apiToken = refreshApiToken();
|
|
}
|
|
// 创建云主机
|
|
String networkId = nextNetworkId();
|
|
log.info("创建云主机:port:{}, networkId:{}", port, networkId);
|
|
CreateCloudHostDto createCloudHostDto = JCloudUtil.oneStepCreateCloudHost(apiToken, imageId, networkId);
|
|
// 内网穿透
|
|
intranetThrough(port, createCloudHostDto.getUsername(), createCloudHostDto.getPassword(), createCloudHostDto.getFloatingIp());
|
|
|
|
createCloudHostDto.setPort(port);
|
|
createCloudHostDto.setIp(frpcHost);
|
|
|
|
return createCloudHostDto;
|
|
}
|
|
|
|
|
|
/**
|
|
* 重置云主机
|
|
*
|
|
* @param resetCloudHostParam
|
|
* @return
|
|
*/
|
|
public boolean resetCloudHost(ResetCloudHostParam resetCloudHostParam) {
|
|
String apiToken = guavaCache.getIfPresent(CommonConstants.API_TOKEN);
|
|
if (StringUtils.isBlank(apiToken)) {
|
|
apiToken = refreshApiToken();
|
|
}
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
JCloudUtil.oneStepRebuildCloudHost(apiToken, imageId, resetCloudHostParam);
|
|
log.info("重新构建云主机serverId:{},耗时:{}", resetCloudHostParam.getServerId(), (System.currentTimeMillis() - startTime));
|
|
|
|
// 重置之后需要重新配置内网穿透
|
|
intranetThrough(resetCloudHostParam.getPort(), resetCloudHostParam.getUsername(), resetCloudHostParam.getPassword(), resetCloudHostParam.getFloatingIp());
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 重置云主机
|
|
*
|
|
* @param serverId
|
|
* @param floatingIpId
|
|
* @return
|
|
*/
|
|
public boolean destroyCloudHost(String serverId, String floatingIpId) {
|
|
String apiToken = guavaCache.getIfPresent(CommonConstants.API_TOKEN);
|
|
if (StringUtils.isBlank(apiToken)) {
|
|
apiToken = refreshApiToken();
|
|
}
|
|
JCloudUtil.oneStepDestroyCloudHost(apiToken, serverId, floatingIpId);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* 刷新token
|
|
*/
|
|
private String refreshApiToken() {
|
|
String apiToken = JCloudUtil.getApiToken(username, password);
|
|
log.info("token refresh--------{}", apiToken);
|
|
guavaCache.put(CommonConstants.API_TOKEN, apiToken);
|
|
|
|
return apiToken;
|
|
}
|
|
|
|
/**
|
|
* 内网穿透
|
|
*
|
|
* @param port
|
|
* @param username
|
|
* @param password
|
|
* @param floatingIp
|
|
*/
|
|
private void intranetThrough(int port, String username, String password, String floatingIp) {
|
|
// 休眠一下是有时候出现这个问题 connect to host 10.119.5.5 port 22: No route to host
|
|
SleepUtil.sleep(3 * 1000);
|
|
String template = frpcTemplate.replaceAll("\\{param1}", frpcHost).replaceAll("\\{param2}", String.valueOf(port));
|
|
|
|
// 通过浮动ip+port 连接ssh执行脚本
|
|
String command = StringUtils.join("sshpass -p ", password, " ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ",
|
|
username, "@", floatingIp, " \'",
|
|
"cd /root/frp_0.44.0_linux_amd64; sudo echo -e \"", template, "\" > frpc.ini ; cat frpc.ini; nohup ./frpc >& cataline.log 2>&1 &", "\'");
|
|
String uuid = RandomUtil.randomString(16);
|
|
log.info("id{},配置内网穿透命令:{}", uuid, command);
|
|
String execResult = ShellUtil.execute(command);
|
|
log.info("id{},执行结果:{}", uuid, execResult);
|
|
}
|
|
|
|
/**
|
|
* 获取一个网络id
|
|
*
|
|
* @return
|
|
*/
|
|
public String nextNetworkId() {
|
|
|
|
if (networkIdList == null) {
|
|
synchronized (networkLock) {
|
|
if (networkIdList == null) {
|
|
String[] split = networkIds.split(";");
|
|
networkIdList = new ArrayList(split.length);
|
|
networkIdList.addAll(Arrays.asList(split));
|
|
}
|
|
}
|
|
}
|
|
synchronized (lock) {
|
|
if (index + 1 > networkIdList.size()) {
|
|
index = 0;
|
|
}
|
|
return networkIdList.get(index++);
|
|
}
|
|
}
|
|
|
|
}
|