diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpCommonUtil.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpCommonUtil.java index e59c3368db203b402ec67478d3bf8d1e595c85cc..d53bb044cc813a06fa69ddf0c77cc04e0edabd9f 100644 --- a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpCommonUtil.java +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpCommonUtil.java @@ -3,7 +3,9 @@ package com.hnkylin.cloud.core.common; import com.hnkylin.cloud.core.enums.ArchitectureType; import com.hnkylin.cloud.core.enums.McArchitectureType; +import java.io.FileInputStream; import java.util.Objects; +import java.util.Properties; public class KcpCommonUtil { public static ArchitectureType changeToKcpArchitectureType(String plateformType) { @@ -25,5 +27,21 @@ public class KcpCommonUtil { } return ArchitectureType.X86_64; } + /** + * 获取主备配置文件中值 + * + * @param key + * @return + */ + public static String getHaConfigInfo(String filePath, String key) { + String value = null; + try { + Properties p = new Properties(); + p.load(new FileInputStream(filePath)); + value = p.getProperty(key); + } catch (Exception e) { + } + return value; + } } diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpResponseData.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpResponseData.java new file mode 100644 index 0000000000000000000000000000000000000000..b5ef5aada5e5101822cce88e503a91e6c7e69d3a --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KcpResponseData.java @@ -0,0 +1,21 @@ +package com.hnkylin.cloud.core.common; + +import lombok.Data; + +@Data +public class KcpResponseData { + /** + * 请求结果编码 + */ + private int code; + /** + * 请求结果描述 + */ + private String desc; + /** + * 请求结果数据 + */ + private T data; + + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KylinCommonConstants.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KylinCommonConstants.java index c6b5d1baa4c6124e6f27066981794ce69bc020e9..90298d747a282b721b77a316b901a97a6b1386c2 100644 --- a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KylinCommonConstants.java +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/common/KylinCommonConstants.java @@ -46,4 +46,17 @@ public interface KylinCommonConstants { int MC_MASTER_PORT = 8443; + String KCP_OPEN_API_SECRET = "kylinsec@kcp"; + + String KCP_OPEN_API_INVOKE_TIME = "INVOKE_TIME"; + + String KCP_OPEN_API_TOKEN = "KCP_OPEN_API_TOKEN"; + + String PERCENT_100 = "100"; + + String PERCENT_0 = "0"; + + String PERCENT_10 = "10"; + + String KCP_PORT = ":5001"; } \ No newline at end of file diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/config/KcpHaProperties.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/config/KcpHaProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..80ebb492dbf9676a9760ffdb0b2a474b776b5b0d --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/config/KcpHaProperties.java @@ -0,0 +1,40 @@ +package com.hnkylin.cloud.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + + +@Component +@ConfigurationProperties("kcpha") +@Data +public class KcpHaProperties { + + + private String role; + + //获取备kcp状态 + private String slaveKcpInfo; + + private String checkPassword; + + private String resetSlave; + + //配置文件路径 + private String configPath; + + private String initSlave; + + //初始化从节点脚本 + private String initSlaveShell; + + //主节点变成从节点脚本 + private String masterToSlaveShell; + + //从节点变成主节点脚本 + private String slaveToMasterShell; + + //重置备节点脚本 + private String resetSlaveShell; + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/domain/KcpHaNodeDo.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/domain/KcpHaNodeDo.java new file mode 100644 index 0000000000000000000000000000000000000000..af297ff8332209ae7c4a883bf1ec75818f02be3f --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/domain/KcpHaNodeDo.java @@ -0,0 +1,25 @@ +package com.hnkylin.cloud.core.domain; + + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName("cloud_ha_node") +public class KcpHaNodeDo extends BaseDo { + + + private String ipAddress; + + private String nodeType; + + private String status; + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/AddSlaveKcpParam.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/AddSlaveKcpParam.java new file mode 100644 index 0000000000000000000000000000000000000000..d47905150dc000dd636861c436a232f1f1f5f6a6 --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/AddSlaveKcpParam.java @@ -0,0 +1,17 @@ +package com.hnkylin.cloud.core.entity.req.kcpha; + +import com.hnkylin.cloud.core.annotation.FieldCheck; +import lombok.Data; + +@Data +public class AddSlaveKcpParam { + + @FieldCheck(notNull = true, notNullMessage = "ip地址不能为空") + private String slaveIp; + + @FieldCheck(notNull = true, notNullMessage = "管理员账号不能为空") + private String sysadmin; + + @FieldCheck(notNull = true, notNullMessage = "密码不能为空") + private String password; +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/ChangeKcpRoleParam.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/ChangeKcpRoleParam.java new file mode 100644 index 0000000000000000000000000000000000000000..c952d16a048e6bf9a0bf905318e963a4a1de6e1a --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/req/kcpha/ChangeKcpRoleParam.java @@ -0,0 +1,10 @@ +package com.hnkylin.cloud.core.entity.req.kcpha; + +import lombok.Data; + +@Data +public class ChangeKcpRoleParam { + + + private String masterKcpIp; +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/AddSlaveResp.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/AddSlaveResp.java new file mode 100644 index 0000000000000000000000000000000000000000..9d6c20eb0c903f037a35b7274f27bd735b50d72b --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/AddSlaveResp.java @@ -0,0 +1,10 @@ +package com.hnkylin.cloud.core.entity.resp.kcpha; + +import lombok.Data; + +@Data +public class AddSlaveResp { + + private Boolean addResult; + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpHaResp.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpHaResp.java new file mode 100644 index 0000000000000000000000000000000000000000..bb14ed0c12c0b8e646f5cfad97f230b808efa17b --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpHaResp.java @@ -0,0 +1,16 @@ +package com.hnkylin.cloud.core.entity.resp.kcpha; + +import lombok.Data; + +@Data +public class KcpHaResp { + + private String nodeRole; + + private String masterIp; + + private String slaveIp; + + private String status; + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpNodeResp.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpNodeResp.java new file mode 100644 index 0000000000000000000000000000000000000000..7c42e70d75ee5050841d515e33d0365a71f56eea --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/entity/resp/kcpha/KcpNodeResp.java @@ -0,0 +1,15 @@ +package com.hnkylin.cloud.core.entity.resp.kcpha; + +import lombok.Data; + +@Data +public class KcpNodeResp { + + + + private String ipAddress; + + private String nodeType; + + private String status; +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeRole.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeRole.java new file mode 100644 index 0000000000000000000000000000000000000000..e4ecbe50b10e0b673675e72d01865dc0e6214431 --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeRole.java @@ -0,0 +1,8 @@ +package com.hnkylin.cloud.core.enums; + +public enum KcpHaNodeRole { + master, + slave; + + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeStatus.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..ae6c43bdab3cca0f4360048cdf3fa4dc52ea381c --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/KcpHaNodeStatus.java @@ -0,0 +1,10 @@ +package com.hnkylin.cloud.core.enums; + +public enum KcpHaNodeStatus { + ONLINE, + INIT, + OFFLINE, + ; + + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogAction.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogAction.java index 07bce5958cac9b18b88bd4309b8a6c547f7f735f..b370330cc0be372e3fd45138488554ac33b29c9e 100644 --- a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogAction.java +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogAction.java @@ -49,6 +49,8 @@ public enum OperateLogAction { SERVERVM_CREATESNAPSHOT("创建快照", OperateLogType.SERVERVM), SERVERVM_APPLYSNAPSHOT("恢复快照", OperateLogType.SERVERVM), SERVERVM_DELETESNAPSHOT("删除快照", OperateLogType.SERVERVM), + KCP_HA_ADD_SLAVE("添加备KCP", OperateLogType.KCP_HA), + KCP_HA_DELETE_SLAVE("删除备KCP", OperateLogType.KCP_HA), ; diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogType.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogType.java index 24d18894ab8dedf2b74d9cb6cc4df429ac47bedc..b907390f6b98ac0a085153608f2e902a6a8a8342 100644 --- a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogType.java +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/enums/OperateLogType.java @@ -10,6 +10,7 @@ public enum OperateLogType { ROLE(6, "角色"), SERVERVM(8, "云服务器"), ALARM(9, "告警"), + KCP_HA(12, "主备KCP"), ; diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/mapper/CloudHaNodeMapper.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/mapper/CloudHaNodeMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..ad54b83758a431620d2ef8cdc348600890fd4322 --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/mapper/CloudHaNodeMapper.java @@ -0,0 +1,7 @@ +package com.hnkylin.cloud.core.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hnkylin.cloud.core.domain.KcpHaNodeDo; + +public interface CloudHaNodeMapper extends BaseMapper { +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/CloudHaNodeService.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/CloudHaNodeService.java new file mode 100644 index 0000000000000000000000000000000000000000..abf3a4b3af890795243d9d9e16add854e7a757e8 --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/CloudHaNodeService.java @@ -0,0 +1,17 @@ +package com.hnkylin.cloud.core.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.hnkylin.cloud.core.domain.KcpHaNodeDo; +import com.hnkylin.cloud.core.enums.KcpHaNodeRole; + +public interface CloudHaNodeService extends IService { + + /** + * 查询备节点KCP + * + * @return + */ + KcpHaNodeDo getKcpNodeByNodeType(KcpHaNodeRole kcpHaNodeRole); + + +} diff --git a/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/impl/CloudHaNodeServiceImpl.java b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/impl/CloudHaNodeServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f8022c404257e58ccb57cb844937d2376a57e186 --- /dev/null +++ b/mcp-cloud-core/src/main/java/com/hnkylin/cloud/core/service/impl/CloudHaNodeServiceImpl.java @@ -0,0 +1,28 @@ +package com.hnkylin.cloud.core.service.impl; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.hnkylin.cloud.core.domain.KcpHaNodeDo; +import com.hnkylin.cloud.core.enums.KcpHaNodeRole; +import com.hnkylin.cloud.core.mapper.CloudHaNodeMapper; +import com.hnkylin.cloud.core.service.CloudHaNodeService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CloudHaNodeServiceImpl extends ServiceImpl + implements CloudHaNodeService { + + + @Override + public KcpHaNodeDo getKcpNodeByNodeType(KcpHaNodeRole kcpHaNodeRole) { + KcpHaNodeDo kcpHaNodeDo = new KcpHaNodeDo(); + kcpHaNodeDo.setDeleteFlag(false); + kcpHaNodeDo.setNodeType(kcpHaNodeRole.name()); + Wrapper wrapper = new QueryWrapper<>(kcpHaNodeDo); + List kcoNodeList = baseMapper.selectList(wrapper); + return kcoNodeList.isEmpty() ? null : kcoNodeList.get(0); + } +} diff --git a/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/constant/KylinHttpResponseHAConstants.java b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/constant/KylinHttpResponseHAConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..727593c8c4f87a224a553785e20b86c604ee4fe7 --- /dev/null +++ b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/constant/KylinHttpResponseHAConstants.java @@ -0,0 +1,21 @@ +package com.hnkylin.cloud.manage.constant; + +/** + * Created by kylin-ksvd on 2023-01-04. + */ +public interface KylinHttpResponseHAConstants { + + String EXIST_SLAVE = "已经存在备kcp,不能加入多个备kcp"; + + String ADD_SLAVE_ERROR = "添加备KCP失败,请联系技术支持人员"; + + String SLAVE_PASSWORD_ERROR = "备KCP密码错误,请重新输入"; + + String CHANGE_MASTER_ERROR = "备kcp升级为主kcp失败,请联系技术支持人员"; + + String MASTER_ONLINE_NOT_CHANGE = "主kcp在线,不能将备KCP升级为主KCP"; + + String NOT_DELETE_SLAVE = "当前状态不能删除备kcp"; + + +} diff --git a/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/ctrl/KcpHaCtrl.java b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/ctrl/KcpHaCtrl.java new file mode 100644 index 0000000000000000000000000000000000000000..a3c61705b9406c1f994898b0ce501c82439abe81 --- /dev/null +++ b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/ctrl/KcpHaCtrl.java @@ -0,0 +1,88 @@ +package com.hnkylin.cloud.manage.ctrl; + +import com.hnkylin.cloud.core.annotation.LoginUser; +import com.hnkylin.cloud.core.annotation.ModelCheck; +import com.hnkylin.cloud.core.annotation.ParamCheck; +import com.hnkylin.cloud.core.common.BaseResult; +import com.hnkylin.cloud.core.entity.req.kcpha.AddSlaveKcpParam; +import com.hnkylin.cloud.core.entity.req.kcpha.ChangeKcpRoleParam; +import com.hnkylin.cloud.core.entity.resp.kcpha.AddSlaveResp; +import com.hnkylin.cloud.core.entity.resp.kcpha.KcpHaResp; +import com.hnkylin.cloud.core.entity.resp.kcpha.KcpNodeResp; +import com.hnkylin.cloud.manage.entity.LoginUserVo; +import com.hnkylin.cloud.manage.service.KcpHaService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +@RequestMapping("/api/ha") +@Slf4j +public class KcpHaCtrl { + + @Resource + private KcpHaService kcpHaService; + + @PostMapping("/slaveKcpInfo") + public BaseResult getSlaveKcpInfo() { + + return BaseResult.success(kcpHaService.getSlaveKcpInfo()); + + } + + @PostMapping("/initSlave") + public BaseResult initSlave(@RequestBody ChangeKcpRoleParam addSlaveKcpParam) { + kcpHaService.initSlave(addSlaveKcpParam); + return BaseResult.success(null); + + } + + @PostMapping("/checkNameAndPassword") + public BaseResult checkNameAndPassword(@RequestBody AddSlaveKcpParam addSlaveKcpParam) { + + return BaseResult.success(kcpHaService.checkNameAndPassword(addSlaveKcpParam)); + + } + + @PostMapping("/resetSlave") + public BaseResult resetSlave() { + kcpHaService.resetSlave(); + return BaseResult.success(null); + + } + + @PostMapping("/addSlave") + @ParamCheck + public BaseResult addSlave(@ModelCheck(notNull = true) @RequestBody AddSlaveKcpParam addSlaveKcpParam, + @LoginUser com.hnkylin.cloud.manage.entity.LoginUserVo loginUserVo) { + kcpHaService.addSlave(addSlaveKcpParam, loginUserVo); + return BaseResult.success(null); + } + + + @PostMapping("/nodeList") + public BaseResult> nodeList() { + + return BaseResult.success(kcpHaService.nodeList()); + } + + + @PostMapping("/changeToMaster") + public BaseResult changeToMaster() { + kcpHaService.changeToMaster(); + return BaseResult.success(null); + } + + @PostMapping("/deleteSlave") + public BaseResult deleteSlave(@LoginUser LoginUserVo loginUserVo) { + kcpHaService.deleteSlave(loginUserVo); + return BaseResult.success(null); + } + + +} diff --git a/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/entity/LoginUserVo.java b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/entity/LoginUserVo.java index 10a4192e283f84bf7ddbbfbd6057fcb5c2b03ee1..51e2ff5d0649ffa9b04bca9d88955b7ceae49113 100644 --- a/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/entity/LoginUserVo.java +++ b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/entity/LoginUserVo.java @@ -11,4 +11,6 @@ public class LoginUserVo { private String token; + private String clientIp; + } diff --git a/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/service/KcpHaService.java b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/service/KcpHaService.java new file mode 100644 index 0000000000000000000000000000000000000000..f31d029fb341cf91ebcd9166ee869b0c3533952e --- /dev/null +++ b/mcp-cloud-manage/src/main/java/com/hnkylin/cloud/manage/service/KcpHaService.java @@ -0,0 +1,609 @@ +package com.hnkylin.cloud.manage.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.hnkylin.cloud.core.common.*; +import com.hnkylin.cloud.core.config.KcpHaProperties; +import com.hnkylin.cloud.core.config.exception.KylinException; +import com.hnkylin.cloud.core.domain.CloudOperateLogDo; +import com.hnkylin.cloud.core.domain.CloudUserDo; +import com.hnkylin.cloud.core.domain.KcpHaNodeDo; +import com.hnkylin.cloud.core.entity.req.kcpha.AddSlaveKcpParam; +import com.hnkylin.cloud.core.entity.req.kcpha.ChangeKcpRoleParam; +import com.hnkylin.cloud.core.entity.resp.kcpha.AddSlaveResp; +import com.hnkylin.cloud.core.entity.resp.kcpha.KcpHaResp; +import com.hnkylin.cloud.core.entity.resp.kcpha.KcpNodeResp; +import com.hnkylin.cloud.core.enums.*; +import com.hnkylin.cloud.core.service.CloudHaNodeService; +import com.hnkylin.cloud.core.service.CloudOperateLogService; +import com.hnkylin.cloud.core.service.CloudUserService; +import com.hnkylin.cloud.manage.constant.KylinHttpResponseHAConstants; +import com.hnkylin.cloud.manage.entity.LoginUserVo; +import jdk.nashorn.api.scripting.JSObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.*; + +@Service +@Slf4j +public class KcpHaService { + + + @Resource + private RestTemplate httpsRestTemplate; + + @Resource + private KcpHaProperties kcpHaProperties; + + @Resource + private CloudUserService cloudUserService; + + @Resource + private CloudHaNodeService cloudHaNodeService; + + @Resource + private CloudOperateLogService cloudOperateLogService; + + + public KcpHaResp getSlaveKcpInfo() { + KcpHaResp kcpHaResp = new KcpHaResp(); + kcpHaResp.setNodeRole(getHaConfigInfo("role")); + if (Objects.equals(kcpHaProperties.getRole(), KcpHaNodeRole.master.name())) { + KcpHaNodeDo slaveNode = cloudHaNodeService.getKcpNodeByNodeType(KcpHaNodeRole.slave); + if (Objects.nonNull(slaveNode)) { + kcpHaResp.setSlaveIp(slaveNode.getIpAddress()); + } + kcpHaResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + } else { + try { + KcpHaNodeDo masterNode = cloudHaNodeService.getKcpNodeByNodeType(KcpHaNodeRole.master); + if (Objects.nonNull(masterNode)) { + kcpHaResp.setMasterIp(masterNode.getIpAddress()); + kcpHaResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + } else { + kcpHaResp.setMasterIp(getHaConfigInfo("master")); + kcpHaResp.setStatus(KcpHaNodeStatus.INIT.name()); + } + } catch (Exception e) { + log.info("getSlaveKcpInfo-error", e); + kcpHaResp.setMasterIp(getHaConfigInfo("master")); + kcpHaResp.setStatus(KcpHaNodeStatus.INIT.name()); + } + } + + return kcpHaResp; + } + + + /** + * 获取主备配置文件中值 + * + * @param key + * @return + */ + public String getHaConfigInfo(String key) { + return KcpCommonUtil.getHaConfigInfo(kcpHaProperties.getConfigPath(), key); + } + + /** + * 调用另一个kcp节点,获取节点状态 + * + * @param kcpNodeIp + * @return + */ + public KcpHaResp getOtherKcpInfo(String kcpNodeIp) { + + KcpHaResp kcpHaResp = null; + KcpResponseData result = sendOtherKcp(kcpNodeIp, null, + kcpHaProperties.getSlaveKcpInfo()); + if (Objects.nonNull(result) && Objects.equals(result.getCode(), HttpCode.SUCCESS)) { + kcpHaResp = JSON.parseObject(JSON.toJSONString(result.getData()), + KcpHaResp.class); + } + return kcpHaResp; + } + + + /** + * 通用请求另一个kcp请求 + * + * @param kcpNodeIp + * @param requestObj + * @param httpUrl + * @return + */ + private KcpResponseData sendOtherKcp(String kcpNodeIp, Object requestObj, String httpUrl) { + KcpResponseData result = null; + try { + HttpHeaders httpHeaders = createKcpHeaders(); + log.info("sendToKcp-headers:{}", JSON.toJSONString(httpHeaders)); + requestObj = Objects.isNull(requestObj) ? new Object() : requestObj; + JSONObject reqBodyObj = JSONObject.parseObject(JSON.toJSONString(requestObj)); + + HttpEntity httpEntity = new HttpEntity(reqBodyObj, httpHeaders); + + String requestUrl = + HttpTypes.HTTPS.getValue() + kcpNodeIp + KylinCommonConstants.KCP_PORT + httpUrl; + log.info("sendOtherKcp-url:{},body:{}", requestUrl, reqBodyObj.toJSONString()); + result = httpsRestTemplate.postForObject(requestUrl, httpEntity, + KcpResponseData.class); + log.info("sendToKcp-response:{}", JSON.toJSONString(result)); + } catch (Exception exception) { + log.info("sendOtherKcp-" + httpUrl + "-error", exception); + } + return result; + } + + /** + * 封装主备KCP 请求头 + * + * @return + */ + private HttpHeaders createKcpHeaders() { + Long current = System.currentTimeMillis(); + String authToken = SHAUtil.getSHA256(current + KylinCommonConstants.KCP_OPEN_API_SECRET); + HttpHeaders headers = new HttpHeaders(); + headers.add(KylinCommonConstants.KCP_OPEN_API_INVOKE_TIME, current.toString()); + headers.add(KylinCommonConstants.KCP_OPEN_API_TOKEN, authToken); + return headers; + } + + + /** + * 主节点变成备节点 + * + * @param kcpHaNodeDo + */ + public void masterToSlave(KcpHaNodeDo kcpHaNodeDo) { + try { + log.info("start-masterToSlave:" + kcpHaNodeDo.getIpAddress()); + ProcessBuilder pb = new ProcessBuilder("sh", kcpHaProperties.getMasterToSlaveShell(), + kcpHaNodeDo.getIpAddress()); + + pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + pb.redirectInput(ProcessBuilder.Redirect.INHERIT); + pb.start(); + } catch (Exception e) { + log.error("initSlave-error", e); + } + } + + /** + * 添加备kcp + * + * @param addSlaveKcpParam + * @param loginUserVo + */ + public void addSlave(AddSlaveKcpParam addSlaveKcpParam, LoginUserVo loginUserVo) { + + //先判断是否已经加入过备节点 + KcpHaNodeDo kcpHaNodeDo = new KcpHaNodeDo(); + kcpHaNodeDo.setDeleteFlag(false); + Wrapper wrapper = new QueryWrapper<>(kcpHaNodeDo); + List kcoNodeList = cloudHaNodeService.list(wrapper); + + KcpHaNodeDo slaveNode = kcoNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.slave.name())).findFirst().orElse(null); + if (Objects.nonNull(slaveNode)) { + throw new KylinException(KylinHttpResponseHAConstants.EXIST_SLAVE); + } + + String localIp = getLocalIp(); + if (Objects.equals(localIp, addSlaveKcpParam.getSlaveIp())) { + throw new KylinException(KylinHttpResponseHAConstants.ADD_SLAVE_ERROR); + } + + KcpHaNodeDo masterNode = kcoNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.master.name())).findFirst().orElse(null); + + //请求备节点KCP,校验账号密码 + AddSlaveResp addSlaveResp = null; + KcpResponseData result = sendOtherKcp(addSlaveKcpParam.getSlaveIp(), addSlaveKcpParam, + kcpHaProperties.getCheckPassword()); + if (Objects.nonNull(result) && Objects.equals(result.getCode(), HttpCode.SUCCESS)) { + addSlaveResp = JSON.parseObject(JSON.toJSONString(result.getData()), + AddSlaveResp.class); + if (!addSlaveResp.getAddResult()) { + throw new KylinException(KylinHttpResponseHAConstants.SLAVE_PASSWORD_ERROR); + } + + + //请求备kcp进行初始化操作 + ChangeKcpRoleParam kcpRoleParam = new ChangeKcpRoleParam(); + kcpRoleParam.setMasterKcpIp(masterNode.getIpAddress()); + + KcpResponseData initSlaveResult = sendOtherKcp(addSlaveKcpParam.getSlaveIp(), kcpRoleParam, + kcpHaProperties.getInitSlave()); + + if (Objects.nonNull(initSlaveResult) && Objects.equals(initSlaveResult.getCode(), HttpCode.SUCCESS)) { + slaveNode = new KcpHaNodeDo(); + slaveNode.setIpAddress(addSlaveKcpParam.getSlaveIp()); + slaveNode.setNodeType(KcpHaNodeRole.slave.name()); + slaveNode.setStatus(KcpHaNodeStatus.INIT.name()); + slaveNode.setCreateBy(loginUserVo.getUserId()); + slaveNode.setCreateTime(new Date()); + cloudHaNodeService.save(slaveNode); + createKcpHaLog(slaveNode, loginUserVo, OperateLogAction.KCP_HA_ADD_SLAVE); + } else { + throw new KylinException(KylinHttpResponseHAConstants.ADD_SLAVE_ERROR); + } + } else { + throw new KylinException(KylinHttpResponseHAConstants.ADD_SLAVE_ERROR); + } + + } + + + /** + * 插入容灾计划站点日志 + */ + private CloudOperateLogDo createKcpHaLog(KcpHaNodeDo kcpHaNodeDo, LoginUserVo loginUserVo, + OperateLogAction action) { + CloudOperateLogDo operateLogDo = new CloudOperateLogDo(); + operateLogDo.setParentId(0); + operateLogDo.setType(OperateLogType.KCP_HA.name()); + operateLogDo.setAction(action.name()); + operateLogDo.setStatus(OperateLogStatus.SUCCESS.name()); + operateLogDo.setPercent(KylinCommonConstants.PERCENT_100); + operateLogDo.setObjId(kcpHaNodeDo.getId()); + operateLogDo.setObjName(kcpHaNodeDo.getIpAddress()); + operateLogDo.setMcTaskId(0L); + operateLogDo.setDetail(kcpHaNodeDo.getIpAddress()); + operateLogDo.setClusterId(0); + operateLogDo.setClientIp(loginUserVo.getClientIp()); + operateLogDo.setCreateBy(loginUserVo.getUserId()); + operateLogDo.setCreateTime(new Date()); + operateLogDo.setEndTime(new Date()); + cloudOperateLogService.save(operateLogDo); + return operateLogDo; + } + + /** + * 校验账号密码 + * + * @param addSlaveKcpParam + */ + public AddSlaveResp checkNameAndPassword(AddSlaveKcpParam addSlaveKcpParam) { + CloudUserDo userDo = cloudUserService.queryUserByUserName(addSlaveKcpParam.getSysadmin()); + AddSlaveResp addSlaveResp = new AddSlaveResp(); + addSlaveResp.setAddResult(false); + if (Objects.equals(addSlaveKcpParam.getPassword(), userDo.getPassword())) { + addSlaveResp.setAddResult(true); + } + return addSlaveResp; + } + + /** + * 初始化备KCP + * + * @param addSlaveKcpParam + */ + public void initSlave(ChangeKcpRoleParam addSlaveKcpParam) { + log.info("start-init-slave,masteIp:" + addSlaveKcpParam.getMasterKcpIp()); + + String threadName = "initSlave-thread"; + new Thread(() -> { + try { + + ProcessBuilder pb = new ProcessBuilder("sh", kcpHaProperties.getInitSlaveShell(), + addSlaveKcpParam.getMasterKcpIp()); + pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + pb.redirectInput(ProcessBuilder.Redirect.INHERIT); + pb.start(); + } catch (Exception e) { + log.error("initSlave-error", e); + } + }, threadName).start(); + + } + + /** + * 主kcp删除了备KCP后,备KCP需要进行重置, + */ + public void resetSlave() { + log.info("resetSlave:"); + + String threadName = "resetSlave-thread"; + new Thread(() -> { + try { + ProcessBuilder pb = new ProcessBuilder("sh", kcpHaProperties.getResetSlaveShell()); + pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + pb.redirectInput(ProcessBuilder.Redirect.INHERIT); + pb.start(); + } catch (Exception e) { + log.error("resetSlave-error", e); + } + }, threadName).start(); + } + + /** + * 主备kcp列表 + * + * @return + */ + public List nodeList() { + + List nodeRespList = new ArrayList<>(); + + if (Objects.equals(kcpHaProperties.getRole(), KcpHaNodeRole.master.name())) { + nodeRespList = masterNodeList(); + } else { + nodeRespList = slaveNodeList(); + } + return nodeRespList; + } + + /** + * 主节点-获取列表 + * + * @return + */ + private List masterNodeList() { + + KcpHaNodeDo kcpHaNodeDo = new KcpHaNodeDo(); + kcpHaNodeDo.setDeleteFlag(false); + Wrapper wrapper = new QueryWrapper<>(kcpHaNodeDo); + List kcpNodeList = cloudHaNodeService.list(wrapper); + List nodeRespList = new ArrayList<>(); + + //主kcp + KcpHaNodeDo masterNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.master.name())).findFirst().orElse(null); + + + KcpNodeResp masterNodeResp = new KcpNodeResp(); + masterNodeResp.setIpAddress(masterNode.getIpAddress()); + masterNodeResp.setNodeType(masterNode.getNodeType()); + masterNodeResp.setStatus(masterNode.getStatus()); + //备KCP + KcpHaNodeDo slaveNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.slave.name())).findFirst().orElse(null); + if (Objects.nonNull(slaveNode)) { + KcpNodeResp slaveNodeResp = new KcpNodeResp(); + slaveNodeResp.setIpAddress(slaveNode.getIpAddress()); + slaveNodeResp.setNodeType(slaveNode.getNodeType()); + KcpHaResp kcpHaResp = null; + KcpResponseData slaveNodeInfo = sendOtherKcp(slaveNode.getIpAddress(), null, + kcpHaProperties.getSlaveKcpInfo()); + if (Objects.nonNull(slaveNodeInfo) && Objects.equals(slaveNodeInfo.getCode(), HttpCode.SUCCESS)) { + kcpHaResp = JSON.parseObject(JSON.toJSONString(slaveNodeInfo.getData()), + KcpHaResp.class); + } + + if (!Objects.equals(slaveNode.getStatus(), KcpHaNodeStatus.INIT.name())) { + if (Objects.nonNull(kcpHaResp)) { + slaveNodeResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + } else { + slaveNodeResp.setStatus(KcpHaNodeStatus.OFFLINE.name()); + } + } else { + //备KCP正在初始化中 + if (Objects.isNull(kcpHaResp) || Objects.equals(kcpHaResp.getNodeRole(), KcpHaNodeRole.master.name()) || + Objects.equals(kcpHaResp.getStatus(), KcpHaNodeStatus.INIT.name())) { + masterNodeResp.setStatus(KcpHaNodeStatus.INIT.name()); + slaveNodeResp.setStatus(KcpHaNodeStatus.INIT.name()); + } else { + slaveNode.setStatus(KcpHaNodeStatus.ONLINE.name()); + cloudHaNodeService.updateById(slaveNode); + + masterNodeResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + slaveNodeResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + } + } + nodeRespList.add(masterNodeResp); + nodeRespList.add(slaveNodeResp); + } else { + nodeRespList.add(masterNodeResp); + } + return nodeRespList; + } + + + /** + * 主节点-获取列表 + * + * @return + */ + private List slaveNodeList() { + List kcpNodeList = new ArrayList<>(); + try { + KcpHaNodeDo kcpHaNodeDo = new KcpHaNodeDo(); + kcpHaNodeDo.setDeleteFlag(false); + Wrapper wrapper = new QueryWrapper<>(kcpHaNodeDo); + kcpNodeList = cloudHaNodeService.list(wrapper); + } catch (Exception e) { + kcpNodeList = new ArrayList<>(); + } + + + List nodeRespList = new ArrayList<>(); + + //备KCP + KcpHaNodeDo slaveNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.slave.name())).findFirst().orElse(null); + + + KcpNodeResp slaveNodeResp = new KcpNodeResp(); + KcpNodeResp masterNodeResp = new KcpNodeResp(); + if (Objects.nonNull(slaveNode)) { + slaveNodeResp.setIpAddress(slaveNode.getIpAddress()); + slaveNodeResp.setNodeType(slaveNode.getNodeType()); + slaveNodeResp.setStatus(slaveNode.getStatus()); + //主kcp + KcpHaNodeDo masterNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.master.name())).findFirst().orElse(null); + if (Objects.nonNull(masterNode)) { + masterNodeResp.setIpAddress(masterNode.getIpAddress()); + masterNodeResp.setNodeType(masterNode.getNodeType()); + masterNodeResp.setStatus(masterNode.getStatus()); + + if (!Objects.equals(slaveNode.getStatus(), KcpHaNodeStatus.INIT.name())) { + //调用主节点-获取主节点状态 + KcpResponseData result = sendOtherKcp(masterNode.getIpAddress(), null, + kcpHaProperties.getSlaveKcpInfo()); + KcpHaResp kcpHaResp = null; + if (Objects.nonNull(result) && Objects.equals(result.getCode(), HttpCode.SUCCESS)) { + kcpHaResp = JSON.parseObject(JSON.toJSONString(result.getData()), + KcpHaResp.class); + } + if (Objects.nonNull(kcpHaResp)) { + masterNodeResp.setStatus(KcpHaNodeStatus.ONLINE.name()); + } else { + masterNodeResp.setStatus(KcpHaNodeStatus.OFFLINE.name()); + } + } else { + masterNodeResp.setStatus(KcpHaNodeStatus.INIT.name()); + } + } else { + masterNodeResp.setIpAddress(getHaConfigInfo("master")); + masterNodeResp.setNodeType(KcpHaNodeRole.master.name()); + masterNodeResp.setStatus(KcpHaNodeStatus.INIT.name()); + slaveNode.setStatus(KcpHaNodeStatus.INIT.name()); + } + + } else { + slaveNodeResp.setIpAddress(getLocalIp()); + slaveNodeResp.setNodeType(KcpHaNodeRole.slave.name()); + slaveNode.setStatus(KcpHaNodeStatus.INIT.name()); + + masterNodeResp.setIpAddress(getHaConfigInfo("master")); + masterNodeResp.setNodeType(KcpHaNodeRole.master.name()); + masterNodeResp.setStatus(KcpHaNodeStatus.INIT.name()); + } + nodeRespList.add(masterNodeResp); + nodeRespList.add(slaveNodeResp); + return nodeRespList; + } + + + /** + * 备kcp升级为主kcp + */ + public void changeToMaster() { + KcpHaNodeDo kcpHaNodeDo = new KcpHaNodeDo(); + kcpHaNodeDo.setDeleteFlag(false); + Wrapper wrapper = new QueryWrapper<>(kcpHaNodeDo); + List kcpNodeList = cloudHaNodeService.list(wrapper); + + KcpHaNodeDo masterNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.master.name())).findFirst().orElse(null); + + KcpHaNodeDo slaveNode = kcpNodeList.stream().filter(item -> Objects.equals(item.getNodeType(), + KcpHaNodeRole.slave.name())).findFirst().orElse(null); + + if (Objects.nonNull(masterNode) && Objects.nonNull(slaveNode)) { + + //调用主节点-获取主节点状态 + KcpResponseData result = sendOtherKcp(masterNode.getIpAddress(), null, + kcpHaProperties.getSlaveKcpInfo()); + + //只有主节点离线才能切主 + if (Objects.nonNull(result)) { + throw new KylinException(KylinHttpResponseHAConstants.MASTER_ONLINE_NOT_CHANGE); + } + executeChangeToMasterCommand(masterNode, slaveNode); + + } else { + throw new KylinException(KylinHttpResponseHAConstants.CHANGE_MASTER_ERROR); + } + + } + + private void executeChangeToMasterCommand(KcpHaNodeDo masterNode, KcpHaNodeDo slaveNode) { + log.info("executeChangeToMasterCommand:" + slaveNode.getIpAddress()); + + String threadName = "initSlave-thread"; + new Thread(() -> { + try { + + ProcessBuilder pb = new ProcessBuilder("sh", kcpHaProperties.getSlaveToMasterShell(), + slaveNode.getIpAddress(), masterNode.getIpAddress()); + pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + pb.redirectInput(ProcessBuilder.Redirect.INHERIT); + pb.start(); + } catch (Exception e) { + log.error("initSlave-error", e); + } + }, threadName).start(); + + } + + /** + * 删除备KCP + * + * @param loginUserVo + */ + public void deleteSlave(LoginUserVo loginUserVo) { + + + KcpHaNodeDo slaveNode = cloudHaNodeService.getKcpNodeByNodeType(KcpHaNodeRole.slave); + //初始化状态下,不能删除备kcp + if (Objects.nonNull(slaveNode) && !Objects.equals(slaveNode.getStatus(), KcpHaNodeStatus.INIT.name())) { + //先判断备节点是否在线 + KcpResponseData result = sendOtherKcp(slaveNode.getIpAddress(), null, + kcpHaProperties.getSlaveKcpInfo()); + if (Objects.nonNull(result)) { + //备节点在线,则调用备节点接口。通知其进行数据库重置 + sendOtherKcp(slaveNode.getIpAddress(), null, kcpHaProperties.getResetSlave()); + } + createKcpHaLog(slaveNode, loginUserVo, OperateLogAction.KCP_HA_DELETE_SLAVE); + slaveNode.setDeleteFlag(true); + slaveNode.setDeleteBy(loginUserVo.getUserId()); + cloudHaNodeService.updateById(slaveNode); + } else { + throw new KylinException(KylinHttpResponseHAConstants.NOT_DELETE_SLAVE); + } + } + + /** + * 获取本级IP + * + * @return + */ + public String getLocalIp() { + try { + Enumeration enumeration = NetworkInterface.getNetworkInterfaces(); + while (enumeration.hasMoreElements()) { + NetworkInterface network = (NetworkInterface) enumeration.nextElement(); + // 排除虚拟接口 + if (network.getName().startsWith("virbr") + || // 排除virbr0等虚拟桥接接口 + network.getName().startsWith("docker") + || // 排除Docker创建的虚拟接口 + // 可以添加更多虚拟接口的判断逻辑 + network.isLoopback() + || // 排除回环接口 + network.isVirtual()) { // 排除其他虚拟接口 + continue; + } + Enumeration addresses = network.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = (InetAddress) addresses.nextElement(); + if (address != null && (address instanceof Inet4Address)) { + return address.getHostAddress(); + } + } + } + } catch (Exception e) { + log.error("getLocalIp-error", e); + return null; + } + + return null; + } + +}