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 guavaCache; private Object lock = new Object(); private Object networkLock = new Object(); private int index = 0; private List 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++); } } }