Merge remote-tracking branch 'origin/V1.00' into V1.00
This commit is contained in:
commit
ac024c1176
@ -23,33 +23,34 @@ public class IpWhitelistController {
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "添加IP白名单", description = "新增一条IP白名单记录")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> addWhitelist(@RequestBody IpWhitelistRequest req) {
|
||||
public ApiResponse<Void> addWhitelist(@RequestBody IpWhitelistRequest req) {
|
||||
ipWhitelistService.addWhitelist(req);
|
||||
return ApiResponse.success("添加成功");
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新IP白名单", description = "根据ID更新IP白名单信息")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> updateWhitelist(@RequestBody IpWhitelistRequest req) {
|
||||
public ApiResponse<Void> updateWhitelist(@RequestBody IpWhitelistRequest req) {
|
||||
ipWhitelistService.updateWhitelist(req);
|
||||
return ApiResponse.success("更新成功");
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
@Operation(summary = "删除IP白名单", description = "根据ID删除指定IP白名单")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> deleteWhitelist(@PathVariable Long id) {
|
||||
public ApiResponse<Void> deleteWhitelist(@PathVariable Long id) {
|
||||
ipWhitelistService.deleteWhitelist(id);
|
||||
return ApiResponse.success("删除成功");
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
//todo 要么分页写到@RequestParam,要么改用post
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询IP白名单", description = "支持分页查询IP白名单列表")
|
||||
public ApiResponse<IPage<IpWhitelistResponse>> getWhitelistPage(@RequestBody IpWhitelistRequest req) {
|
||||
IPage<IpWhitelistResponse> page = ipWhitelistService.getWhitelistPage(req);
|
||||
public ApiResponse<IPage<IpWhitelistResponse>> getWhitelistPage(@RequestParam int pageNum,
|
||||
@RequestParam int pageSize) {
|
||||
IPage<IpWhitelistResponse> page = ipWhitelistService.getWhitelistPage(pageNum, pageSize);
|
||||
return ApiResponse.success(page);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +38,9 @@ public class NetworkConfigController {
|
||||
@Operation(summary = "设置IPv4配置", description = "为指定网络接口配置IPv4地址、掩码、网关等信息")
|
||||
@PostMapping("/ipv4-config/set")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> setIpv4Config(@RequestBody Ipv4ConfigRequest req) {
|
||||
String resultMessage = networkConfigService.setIpv4Config(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> setIpv4Config(@RequestBody Ipv4ConfigRequest req) {
|
||||
networkConfigService.setIpv4Config(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取IPv6配置信息", description = "根据设备名称获取指定网络接口的IPv6配置详情")
|
||||
@ -53,9 +53,9 @@ public class NetworkConfigController {
|
||||
@Operation(summary = "设置IPv6配置", description = "为指定网络接口配置IPv6地址、前缀长度、网关等信息")
|
||||
@PostMapping("/ipv6-config/set")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> setIpv6Config(@RequestBody Ipv6ConfigRequest req) {
|
||||
String resultMessage = networkConfigService.setIpv6Config(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> setIpv6Config(@RequestBody Ipv6ConfigRequest req) {
|
||||
networkConfigService.setIpv6Config(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有Bond名称", description = "返回当前系统中所有已创建的Bond接口名称")
|
||||
@ -67,26 +67,26 @@ public class NetworkConfigController {
|
||||
@Operation(summary = "创建Bond", description = "创建一个新的Bond聚合接口,需指定名称、模式和从属接口")
|
||||
@PostMapping("/bond/create")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> createBond(@RequestBody BondCreateRequest req) {
|
||||
String resultMessage = networkConfigService.createBond(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> createBond(@RequestBody BondCreateRequest req) {
|
||||
networkConfigService.createBond(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "删除Bond", description = "根据Bond名称删除指定的聚合接口")
|
||||
@PostMapping("/bond/delete/{bondName}")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> deleteBond(@PathVariable("bondName") String bondName){
|
||||
String resultMessage = networkConfigService.deleteBond(bondName);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> deleteBond(@PathVariable("bondName") String bondName){
|
||||
networkConfigService.deleteBond(bondName);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "添加从属接口到Bond", description = "向指定Bond中添加一个或多个从属网络接口")
|
||||
@PostMapping("/bond-slave/add")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> addSlavesTOBond(@RequestBody BondAddSlavesRequest req) {
|
||||
String resultMessage = networkConfigService.addSlavesTOBond(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> addSlavesTOBond(@RequestBody BondAddSlavesRequest req) {
|
||||
networkConfigService.addSlavesTOBond(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "从Bond中移除从属接口", description = "从指定Bond中移除一个或多个从属网络接口")
|
||||
@ -120,10 +120,9 @@ public class NetworkConfigController {
|
||||
@Operation(summary = "设置Bond模式", description = "修改指定Bond的绑定模式")
|
||||
@PostMapping("/bond/mode/set")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> setBondMode(@RequestBody BondModifyModeRequest req) {
|
||||
|
||||
String resultMessage = networkConfigService.setBondMode(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> setBondMode(@RequestBody BondModifyModeRequest req) {
|
||||
networkConfigService.setBondMode(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取路由表", description = "返回当前系统的IPv4/IPv6路由表信息")
|
||||
@ -136,32 +135,32 @@ public class NetworkConfigController {
|
||||
@Operation(summary = "设置默认路由", description = "配置或修改系统的默认网关路由")
|
||||
@PostMapping("/routes/default/set")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> setDefaultRoute(@RequestBody SetDefaultRouteRequest req) {
|
||||
String resultMessage = networkConfigService.setDefaultRoute(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> setDefaultRoute(@RequestBody SetDefaultRouteRequest req) {
|
||||
networkConfigService.setDefaultRoute(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "添加静态路由", description = "新增一条静态路由规则")
|
||||
@PostMapping("/routes/static/add")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> addStaticRoute(@RequestBody AddStaticRouteRequest req) {
|
||||
String resultMessage = networkConfigService.addStaticRoute(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> addStaticRoute(@RequestBody AddStaticRouteRequest req) {
|
||||
networkConfigService.addStaticRoute(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除默认路由", description = "删除指定的默认路由")
|
||||
@PostMapping("/routes/default/delete")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> deleteDefaultRoute(@RequestBody DeleteDefaultRouteRequest req) {
|
||||
String resultMessage = networkConfigService.deleteDefaultRoute(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> deleteDefaultRoute(@RequestBody DeleteDefaultRouteRequest req) {
|
||||
networkConfigService.deleteDefaultRoute(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除静态路由", description = "删除指定的静态路由")
|
||||
@PostMapping("/routes/static/delete")
|
||||
@ReplayProtected
|
||||
public ApiResponse<String> deleteStaticRoute(@RequestBody DeleteStaticRouteRequest req) {
|
||||
String resultMessage = networkConfigService.deleteStaticRoute(req);
|
||||
return ApiResponse.success(resultMessage);
|
||||
public ApiResponse<Void> deleteStaticRoute(@RequestBody DeleteStaticRouteRequest req) {
|
||||
networkConfigService.deleteStaticRoute(req);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ public class TimeConfigRequest {
|
||||
example = "[\"ntp.aliyun.com\", \"ntp.tencent.com\"]")
|
||||
private List<String> ntpServers;
|
||||
|
||||
@Schema(description = "NTP同步间隔时间 (单位: 秒。默认 600)",
|
||||
example = "600")
|
||||
@Schema(description = "NTP同步间隔时间 (单位: 秒。默认 512)",
|
||||
example = "512")
|
||||
private Integer syncInterval;
|
||||
|
||||
}
|
||||
|
||||
@ -86,6 +86,19 @@ public class IpWhitelistFilter implements Filter {
|
||||
}
|
||||
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
|
||||
//TODO 后续添加网关ip
|
||||
//添加信任的网关ip
|
||||
String[] trustedProxies = {};
|
||||
boolean isTrustedProxy = false;
|
||||
for (String proxy : trustedProxies) {
|
||||
if (remoteAddr.equals(proxy)) {
|
||||
isTrustedProxy = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//todo 这几个header获取的ip可能是客户端伪造的
|
||||
String[] headers = {
|
||||
"X-Forwarded-For",
|
||||
@ -94,13 +107,15 @@ public class IpWhitelistFilter implements Filter {
|
||||
"HTTP_CLIENT_IP",
|
||||
"HTTP_X_FORWARDED_FOR"
|
||||
};
|
||||
if (isTrustedProxy) {
|
||||
for (String header : headers) {
|
||||
String ip = request.getHeader(header);
|
||||
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
|
||||
return ip.split(",")[0].trim();
|
||||
}
|
||||
}
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
return remoteAddr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ public interface IpWhitelistRepository {
|
||||
/**
|
||||
* 分页查询白名单
|
||||
*/
|
||||
Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page, IpWhitelistRequest req);
|
||||
Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page);
|
||||
|
||||
/**
|
||||
* 查询所有白名单
|
||||
|
||||
@ -53,7 +53,7 @@ public class IpWhiltelistRepositoryImpl implements IpWhitelistRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page, IpWhitelistRequest req) {
|
||||
public Page<IpWhitelistEntity> selectPage(Page<IpWhitelistEntity> page) {
|
||||
// LambdaQueryWrapper<IpWhitelistEntity> wrapper = new LambdaQueryWrapper<>();
|
||||
//
|
||||
// if (StringUtils.hasText(req.getIp())) {
|
||||
|
||||
@ -2,6 +2,7 @@ package com.cisd.tms.modules.device.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.modules.device.dto.network.IpWhitelistRequest;
|
||||
import com.cisd.tms.modules.device.dto.network.IpWhitelistResponse;
|
||||
@ -11,6 +12,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@ -19,41 +21,48 @@ public class IpWhitelistService {
|
||||
|
||||
private final IpWhitelistRepository ipWhitelistRepository;
|
||||
|
||||
private static final Pattern IPV4_PATTERN = Pattern.compile(
|
||||
"^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$"
|
||||
);
|
||||
|
||||
private static final Pattern CIDR_PATTERN = Pattern.compile(
|
||||
"^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)/(0|[1-9]|[1-2]\\d|3[0-2])$"
|
||||
);
|
||||
|
||||
public IpWhitelistService(IpWhitelistRepository ipWhitelistRepository) {
|
||||
this.ipWhitelistRepository = ipWhitelistRepository;
|
||||
}
|
||||
|
||||
public void addWhitelist(IpWhitelistRequest req) {
|
||||
// 新增前校验 IP+掩码 是否已存在
|
||||
checkIpRule(req.getIp(), req.getMask(), null);
|
||||
|
||||
IpWhitelistEntity entity = new IpWhitelistEntity();
|
||||
String ip = req.getIp();
|
||||
String mask = req.getMask();
|
||||
checkIpRule(ip, mask, null);
|
||||
|
||||
IpWhitelistEntity entity = new IpWhitelistEntity();
|
||||
|
||||
entity.setIp(ip);
|
||||
entity.setMask(mask);
|
||||
entity.setIp(ip.trim());
|
||||
entity.setMask(mask.trim());
|
||||
|
||||
ipWhitelistRepository.addWhitelist(entity);
|
||||
}
|
||||
//
|
||||
public void updateWhitelist(IpWhitelistRequest req) {
|
||||
if (req.getId() == null || req.getId().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("ID should not be null");
|
||||
throw new IllegalArgumentException("ID 为空");
|
||||
}
|
||||
|
||||
Long idLong = Long.valueOf(req.getId());
|
||||
Long idLong = Long.valueOf(req.getId().trim());
|
||||
checkIpRule(req.getIp(), req.getMask(), idLong);
|
||||
|
||||
Optional<IpWhitelistEntity> entityOpt = ipWhitelistRepository.findById(idLong);
|
||||
if (entityOpt.isEmpty()) {
|
||||
throw new BizException(404, "ID is not exist");
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "ID不存在");
|
||||
}
|
||||
IpWhitelistEntity entity = entityOpt.get();
|
||||
|
||||
entity.setIp(req.getIp());
|
||||
entity.setMask(req.getMask());
|
||||
entity.setIp(req.getIp().trim());
|
||||
entity.setMask(req.getMask().trim());
|
||||
ipWhitelistRepository.updateWhitelist(entity);
|
||||
}
|
||||
|
||||
@ -61,15 +70,15 @@ public class IpWhitelistService {
|
||||
public void deleteWhitelist(Long id) {
|
||||
Optional<IpWhitelistEntity> entityOpt = ipWhitelistRepository.findById(id);
|
||||
if (entityOpt.isEmpty()) {
|
||||
throw new BizException(404, "ID is not exist");
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "ID不存在");
|
||||
}
|
||||
ipWhitelistRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
public IPage<IpWhitelistResponse> getWhitelistPage(IpWhitelistRequest req) {
|
||||
Page<IpWhitelistEntity> page = new Page<>(req.getPageNum(), req.getPageSize());
|
||||
ipWhitelistRepository.selectPage(page, req);
|
||||
public IPage<IpWhitelistResponse> getWhitelistPage(int pageNum, int pageSize) {
|
||||
Page<IpWhitelistEntity> page = new Page<>(pageNum, pageSize);
|
||||
ipWhitelistRepository.selectPage(page);
|
||||
|
||||
|
||||
return page.convert(entity -> {
|
||||
@ -93,13 +102,15 @@ public class IpWhitelistService {
|
||||
*/
|
||||
private void checkIpRule(String ip, String mask, Long excludeId) {
|
||||
if (ip == null || ip.isEmpty()){
|
||||
throw new IllegalArgumentException("ip is empty");
|
||||
throw new IllegalArgumentException("ip地址为空");
|
||||
}
|
||||
if (mask == null || mask.isEmpty()){
|
||||
throw new IllegalArgumentException("mask is empty");
|
||||
throw new IllegalArgumentException("掩码为空");
|
||||
}
|
||||
|
||||
|
||||
if (!IPV4_PATTERN.matcher(ip.trim()).matches()){
|
||||
throw new IllegalArgumentException("非法的 IPv4 地址: " + ip);
|
||||
}
|
||||
|
||||
|
||||
String[] octets = ip.split("\\.");
|
||||
@ -125,7 +136,12 @@ public class IpWhitelistService {
|
||||
|
||||
|
||||
if (ipWhitelistRepository.existsByIpAndMask(ip, mask, excludeId)) {
|
||||
throw new BizException(404, "已存在该 IP 和掩码配置");
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "已存在该 IP 和掩码配置");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String trim(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,14 +11,20 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class NetworkConfigService {
|
||||
|
||||
private static final Pattern IPV4_PATTERN = Pattern.compile(
|
||||
"^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)$"
|
||||
);
|
||||
|
||||
private static final Pattern CIDR_PATTERN = Pattern.compile(
|
||||
"^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)/(0|[1-9]|[1-2]\\d|3[0-2])$"
|
||||
);
|
||||
|
||||
public List<NetworkInfoResponse> getNetworkInfo(){
|
||||
|
||||
@ -70,7 +76,7 @@ public class NetworkConfigService {
|
||||
|
||||
|
||||
public Ipv4InfoResponse getIpv4Info(String deviceName){
|
||||
String connectionName = getConnectionNameByDeviceName(deviceName);
|
||||
String connectionName = getConnectionNameByDeviceName(deviceName.trim());
|
||||
if (connectionName == null){
|
||||
connectionName = deviceName;
|
||||
}
|
||||
@ -121,33 +127,41 @@ public class NetworkConfigService {
|
||||
}
|
||||
|
||||
|
||||
public String setIpv4Config(Ipv4ConfigRequest req){
|
||||
|
||||
if (req.getDeviceName() == null || req.getDeviceName().trim().isEmpty()) {
|
||||
public void setIpv4Config(Ipv4ConfigRequest req){
|
||||
String ipv4 = trim(req.getIpv4());
|
||||
String deviceName = trim(req.getDeviceName());
|
||||
String mask = trim(req.getMaskLength());
|
||||
if (deviceName.isEmpty()) {
|
||||
throw new IllegalArgumentException("网卡名称不能为空");
|
||||
}
|
||||
|
||||
|
||||
if (!isValidIpv4(req.getIpv4())) {
|
||||
if (ipv4.isEmpty()) {
|
||||
throw new IllegalArgumentException("ip地址不能为空");
|
||||
}
|
||||
if (mask.isEmpty()) {
|
||||
throw new IllegalArgumentException("子网掩码长度不能为空");
|
||||
}
|
||||
if (!IPV4_PATTERN.matcher(ipv4).matches()) {
|
||||
throw new IllegalArgumentException("无效的 IP 地址格式: " + req.getIpv4());
|
||||
}
|
||||
|
||||
boolean hasGateway = req.getGateway() != null && !req.getGateway().trim().isEmpty();
|
||||
|
||||
if (hasGateway && !isValidIpv4(req.getGateway())) {
|
||||
if (hasGateway && !IPV4_PATTERN.matcher(req.getGateway().trim()).matches()) {
|
||||
throw new IllegalArgumentException("无效的网关地址格式: " + req.getGateway());
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
//判断连接是否存在,如果不存在则新建新连接
|
||||
boolean isNewConnection = false;
|
||||
String connectionName = getConnectionNameByDeviceName(deviceName);
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
connectionName = deviceName;
|
||||
isNewConnection = true; // 标记为需要执行 add 创建配置
|
||||
}
|
||||
|
||||
|
||||
// 转化掩码格式并判断掩码格式是否正确
|
||||
int maskLength = 0;
|
||||
try {
|
||||
String netmask = req.getMaskLength();
|
||||
if (netmask == null || netmask.isEmpty()) {
|
||||
String netmask = trim(req.getMaskLength());
|
||||
if (netmask.isEmpty()) {
|
||||
throw new IllegalArgumentException("掩码不能为空");
|
||||
}
|
||||
// 传的是数字/24
|
||||
@ -170,9 +184,9 @@ public class NetworkConfigService {
|
||||
}
|
||||
|
||||
//判断ip和网关是否在同一个子网里面
|
||||
int ip1Int = ipToInt(req.getIpv4());
|
||||
int ip1Int = ipToInt(req.getIpv4().trim());
|
||||
if (hasGateway){
|
||||
int ip2Int = ipToInt(req.getGateway());
|
||||
int ip2Int = ipToInt(req.getGateway().trim());
|
||||
int maskInt = (maskLength == 0) ? 0 : (0xFFFFFFFF << (32 - maskLength));
|
||||
if (!((ip1Int & maskInt) == (ip2Int & maskInt))){
|
||||
throw new IllegalArgumentException(
|
||||
@ -184,17 +198,36 @@ public class NetworkConfigService {
|
||||
|
||||
|
||||
String cidr = req.getIpv4() + "/" + maskLength;
|
||||
//todo 这里需要判断hasGateway是否为true,再确定命令是否有 ipv4.gateway。
|
||||
try {
|
||||
if (isNewConnection) {
|
||||
List<String> cmdArgs = new ArrayList<>();
|
||||
cmdArgs.add("nmcli"); cmdArgs.add("con"); cmdArgs.add("add");
|
||||
cmdArgs.add("type"); cmdArgs.add("ethernet"); // 默认创建以太网类型
|
||||
cmdArgs.add("con-name"); cmdArgs.add(connectionName);
|
||||
cmdArgs.add("ifname"); cmdArgs.add(deviceName);
|
||||
cmdArgs.add("ipv4.method"); cmdArgs.add("manual");
|
||||
cmdArgs.add("ipv4.addresses"); cmdArgs.add(cidr);
|
||||
if (hasGateway) {
|
||||
cmdArgs.add("ipv4.gateway");cmdArgs.add(req.getGateway());
|
||||
cmdArgs.add("ipv4.never-default");cmdArgs.add("yes");
|
||||
}
|
||||
executeCommand(cmdArgs.toArray(new String[0]));
|
||||
} else {
|
||||
if (hasGateway) {
|
||||
executeCommand("nmcli", "con", "mod", connectionName,
|
||||
"ipv4.method", "manual",
|
||||
"ipv4.addresses", cidr,
|
||||
"ipv4.gateway", req.getGateway());
|
||||
"ipv4.gateway", req.getGateway(),
|
||||
"ipv4.never-default", "yes");
|
||||
} else {
|
||||
executeCommand("nmcli", "con", "mod", connectionName,
|
||||
"ipv4.method", "manual",
|
||||
"ipv4.addresses", cidr,
|
||||
"ipv4.gateway", "");
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand("nmcli", "con", "up", connectionName);
|
||||
|
||||
return "网络连接 [" + req.getDeviceName() + "] IPv4 配置成功";
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "修改网络配置失败: " + e.getMessage());
|
||||
@ -203,7 +236,7 @@ public class NetworkConfigService {
|
||||
|
||||
|
||||
public Ipv6InfoResponse getIpv6Info(String deviceName){
|
||||
|
||||
deviceName = trim(deviceName);
|
||||
String connectionName = getConnectionNameByDeviceName(deviceName);
|
||||
if (connectionName == null){
|
||||
connectionName = deviceName;
|
||||
@ -238,10 +271,10 @@ public class NetworkConfigService {
|
||||
Ipv6AddressItem item = new Ipv6AddressItem();
|
||||
if (ipAndMaskLength.contains("/")) {
|
||||
String[] parts = ipAndMaskLength.split("/");
|
||||
item.setIpv6(parts[0]);
|
||||
item.setIpv6(parts[0].replace("\\:", ":"));
|
||||
item.setMaskLength(parts[1]);
|
||||
} else {
|
||||
item.setIpv6(ipAndMaskLength);
|
||||
item.setIpv6(ipAndMaskLength.replace("\\:", ":"));
|
||||
}
|
||||
// 将解析好的单个 IP 对象放入集合
|
||||
addressList.add(item);
|
||||
@ -251,7 +284,7 @@ public class NetworkConfigService {
|
||||
|
||||
|
||||
if (lines.size()> 1 && !lines.get(1).trim().isEmpty()) {
|
||||
resp.setGateway(lines.get(1).trim());
|
||||
resp.setGateway(lines.get(1).trim().replace("\\:", ":"));
|
||||
}
|
||||
|
||||
if (lines.size() > 2 && !lines.get(2).trim().isEmpty()) {
|
||||
@ -268,7 +301,8 @@ public class NetworkConfigService {
|
||||
return resp;
|
||||
}
|
||||
|
||||
public String setIpv6Config(Ipv6ConfigRequest req){
|
||||
public void setIpv6Config(Ipv6ConfigRequest req){
|
||||
String deviceName = trim(req.getDeviceName());
|
||||
if (req.getDeviceName() == null || req.getDeviceName().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("网卡名称不能为空");
|
||||
}
|
||||
@ -293,29 +327,47 @@ public class NetworkConfigService {
|
||||
throw new IllegalArgumentException("无效的 IPv6 前缀长度,必须在 0 到 128 之间");
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
// 判断连接是否存在,如果不存在则需要新建
|
||||
boolean isNewConnection = false;
|
||||
String connectionName = getConnectionNameByDeviceName(deviceName);
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
connectionName = deviceName;
|
||||
isNewConnection = true;
|
||||
}
|
||||
|
||||
|
||||
String ipWithPrefix = req.getIpv6() + "/" + req.getMaskLength();
|
||||
String ipWithPrefix = req.getIpv6().trim() + "/" + req.getMaskLength().trim();
|
||||
try {
|
||||
if (isNewConnection) {
|
||||
List<String> cmdArgs = new ArrayList<>();
|
||||
cmdArgs.add("nmcli"); cmdArgs.add("con"); cmdArgs.add("add");
|
||||
cmdArgs.add("type"); cmdArgs.add("ethernet");
|
||||
cmdArgs.add("con-name"); cmdArgs.add(connectionName);
|
||||
cmdArgs.add("ifname"); cmdArgs.add(deviceName);
|
||||
cmdArgs.add("ipv6.method"); cmdArgs.add("manual");
|
||||
cmdArgs.add("ipv6.addresses"); cmdArgs.add(ipWithPrefix);
|
||||
if (hasGateway) {
|
||||
cmdArgs.add("ipv6.gateway"); cmdArgs.add(req.getGateway().trim());
|
||||
cmdArgs.add("ipv6.never-default");cmdArgs.add("yes");
|
||||
}
|
||||
executeCommand(cmdArgs.toArray(new String[0]));
|
||||
} else {
|
||||
if (hasGateway) {
|
||||
executeCommand("nmcli", "con", "mod", connectionName,
|
||||
"ipv6.method", "manual",
|
||||
"ipv6.addresses", ipWithPrefix,
|
||||
"ipv6.gateway", req.getGateway());
|
||||
"ipv6.gateway", req.getGateway().trim(),
|
||||
"ipv6.never-default","yes");
|
||||
} else {
|
||||
executeCommand("nmcli", "con", "mod", connectionName,
|
||||
"ipv6.method", "manual",
|
||||
"ipv6.addresses", ipWithPrefix);
|
||||
"ipv6.addresses", ipWithPrefix,
|
||||
"ipv6.gateway", "");
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand("nmcli", "con", "up", connectionName);
|
||||
|
||||
return "网络连接 [" + req.getDeviceName() + "] IPv6 配置成功";
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "修改 IPv6 网络配置失败: " + e.getMessage());
|
||||
}
|
||||
@ -327,7 +379,7 @@ public class NetworkConfigService {
|
||||
List<String> lines = executeCommand("nmcli", "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device");
|
||||
|
||||
for (String line : lines) {
|
||||
String[] parts = line.split(":", -1);
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
if (parts.length >= 2 && "bond".equals(parts[1])) {
|
||||
bondNames.add(parts[0]);
|
||||
}
|
||||
@ -335,8 +387,9 @@ public class NetworkConfigService {
|
||||
return bondNames;
|
||||
}
|
||||
|
||||
public String createBond(BondCreateRequest req){
|
||||
if (req.getBondName() == null || req.getBondName().trim().isEmpty()) {
|
||||
public void createBond(BondCreateRequest req){
|
||||
String bondName = trim(req.getBondName());
|
||||
if (bondName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
if (req.getMode() == null) {
|
||||
@ -347,8 +400,8 @@ public class NetworkConfigService {
|
||||
try {
|
||||
List<String> lines = executeCommand("nmcli", "-g", "NAME", "con", "show");
|
||||
for (String line : lines) {
|
||||
if (req.getBondName().equals(line.trim())) {
|
||||
throw new RuntimeException("网络连接名称 [" + req.getBondName() + "] 已存在,请勿重复创建");
|
||||
if (bondName.equals(line.trim())) {
|
||||
throw new RuntimeException("网络连接名称 [" + bondName + "] 已存在,请勿重复创建");
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
@ -365,58 +418,93 @@ public class NetworkConfigService {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "add",
|
||||
"type", "bond",
|
||||
"con-name", req.getBondName(),
|
||||
"ifname", req.getBondName(),
|
||||
"con-name", bondName,
|
||||
"ifname", bondName,
|
||||
"bond.options", bondOptions);
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "创建 Bond 失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (req.getIpv4Config() != null) {
|
||||
req.getIpv4Config().setDeviceName(req.getBondName());
|
||||
req.getIpv4Config().setDeviceName(bondName);
|
||||
try {
|
||||
setIpv4Config(req.getIpv4Config());
|
||||
} catch (Exception e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Bond [" + req.getBondName() + "] 创建成功,但 IP 配置失败: " + e.getMessage());
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Bond [" + bondName + "] 创建成功,但 IP 配置失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "Bond [" + req.getBondName() + "] 创建并初始化成功";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String deleteBond(String bondName) {
|
||||
if (bondName == null || bondName.isEmpty()) {
|
||||
public void deleteBond(String bondName) {
|
||||
if (bondName == null || bondName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
|
||||
boolean bondExist = false;
|
||||
bondName = bondName.trim();
|
||||
|
||||
List<String> uuidsToDelete = new ArrayList<>();
|
||||
boolean bondFound = false;
|
||||
|
||||
try {
|
||||
List<String> lines = executeCommand("nmcli", "-g", "NAME", "con", "show");
|
||||
List<String> lines = executeCommand("nmcli", "-t", "-f", "UUID,NAME,TYPE", "con", "show");
|
||||
|
||||
for (String line : lines) {
|
||||
if (bondName.equals(line.trim())) {
|
||||
bondExist = true;
|
||||
if (line.trim().isEmpty()) continue;
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
|
||||
if (parts.length >= 3) {
|
||||
String uuid = parts[0];
|
||||
String name = parts[1].replace("\\:", ":");
|
||||
String type = parts[2];
|
||||
|
||||
// 匹配到 Bond 自身
|
||||
if (bondName.equals(name) && "bond".equals(type)) {
|
||||
bondFound = true;
|
||||
uuidsToDelete.add(uuid);
|
||||
}
|
||||
else if (type != null && type.contains("ethernet")) {
|
||||
try {
|
||||
List<String> masterOutput = executeCommand("nmcli", "-g", "connection.master", "con", "show", uuid);
|
||||
if (!masterOutput.isEmpty() && bondName.equals(masterOutput.get(0).trim())) {
|
||||
uuidsToDelete.add(0, uuid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("查询连接 [{}] 的 master出现异常: {}", uuid, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bondExist) {
|
||||
executeCommand("nmcli", "con", "delete", bondName);
|
||||
} else {
|
||||
if (!bondFound) {
|
||||
throw new RuntimeException("Bond [" + bondName + "] 不存在");
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Bond [" + bondName + "] 删除失败 " + e.getMessage());
|
||||
}
|
||||
return "Bond [" + bondName + "] 删除成功";
|
||||
|
||||
|
||||
for (String targetUuid : uuidsToDelete) {
|
||||
//先停后删
|
||||
try {
|
||||
executeCommand("nmcli", "con", "down", "uuid", targetUuid);
|
||||
} catch (Exception e) {
|
||||
log.warn("停用连接 [{}] 时出现异常: {}", targetUuid, e.getMessage());
|
||||
}
|
||||
|
||||
//todo
|
||||
//这里是先删除物理网卡再添加bond,如果添加失败,那么之前删除的不可逆
|
||||
//建议对添加的bondname和slaveList做存在性校验,再执行添加。 或者记录已删除连接 UUID/名称,失败后回滚。
|
||||
public String addSlavesTOBond(BondAddSlavesRequest req) {
|
||||
executeCommand("nmcli", "con", "delete", "uuid", targetUuid);
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Bond [" + bondName + "] 及从属网卡删除失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addSlavesTOBond(BondAddSlavesRequest req) {
|
||||
String bondName = req.getBondName();
|
||||
List<String> slaves = req.getSlaveList();
|
||||
|
||||
|
||||
if (bondName == null || bondName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
@ -424,26 +512,53 @@ public class NetworkConfigService {
|
||||
throw new IllegalArgumentException("物理网卡列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
List<String> existingConnections;
|
||||
List<String> existingDevices;
|
||||
try {
|
||||
existingConnections = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME", "con", "show");
|
||||
existingConnections = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME,TYPE", "con", "show");
|
||||
existingDevices = executeCommand("nmcli", "-t", "-f", "DEVICE,TYPE", "dev");
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "获取系统网络连接列表失败");
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "获取系统网络状态失败");
|
||||
}
|
||||
|
||||
|
||||
//查询bond是否存在
|
||||
boolean bondExists = false;
|
||||
for (String line : existingConnections) {
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
if (parts.length >= 4) {
|
||||
String name = parts[2].replace("\\:", ":");
|
||||
String type = parts[3];
|
||||
if (bondName.equals(name) && "bond".equals(type)) {
|
||||
bondExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bondExists) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "目标 Bond [" + bondName + "] 不存在");
|
||||
}
|
||||
|
||||
|
||||
for (String phyIf : slaves) {
|
||||
if (phyIf == null || phyIf.trim().isEmpty()){
|
||||
continue;
|
||||
if (phyIf == null || phyIf.trim().isEmpty()) continue;
|
||||
boolean devExists = existingDevices.stream()
|
||||
.map(line -> line.split("(?<!\\\\):", -1)[0].replace("\\:", ":"))
|
||||
.anyMatch(devName -> devName.equals(phyIf));
|
||||
|
||||
if (!devExists) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "当前物理网卡设备 [" + phyIf + "] 在系统中不存在");
|
||||
}
|
||||
}
|
||||
|
||||
for (String line : existingConnections) {
|
||||
if (line.trim().isEmpty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
List<String> uuidsToDown = new ArrayList<>();
|
||||
for (String phyIf : slaves) {
|
||||
if (phyIf == null || phyIf.trim().isEmpty()) continue;
|
||||
String slaveConnectionName = bondName + "-slave-" + phyIf;
|
||||
|
||||
for (String line : existingConnections) {
|
||||
if (line.trim().isEmpty()) continue;
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
if (parts.length >= 3) {
|
||||
String uuid = parts[0];
|
||||
@ -451,60 +566,91 @@ public class NetworkConfigService {
|
||||
String name = parts[2].replace("\\:", ":");
|
||||
|
||||
if (slaveConnectionName.equals(name) || phyIf.equals(device)) {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "delete", "uuid", uuid);
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("清理物理网卡 {} 的旧连接/冲突连接 [{}] 失败: {}", phyIf, name, e.getMessage());
|
||||
uuidsToDown.add(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//先停止后删除
|
||||
for (String uuid : uuidsToDown) {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "modify", "uuid", uuid, "connection.autoconnect", "no");
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("修改旧连接 [{}] 的自启属性失败,该连接可能不存在: {}", uuid, e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
executeCommand("nmcli", "con", "down", "uuid", uuid);
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("暂停旧连接 [{}] 失败,该连接可能已经处于断开状态: {}", uuid, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<String> newlyAddedSlaveNames = new ArrayList<>(); // 记录已经成功添加的,用于添加失败回滚
|
||||
try {
|
||||
for (String phyIf : slaves) {
|
||||
if (phyIf == null || phyIf.trim().isEmpty()) continue;
|
||||
String slaveConnectionName = bondName + "-slave-" + phyIf;
|
||||
|
||||
executeCommand("nmcli", "con", "add",
|
||||
"type", "bond-slave",
|
||||
"con-name", slaveConnectionName,
|
||||
"ifname", phyIf,
|
||||
"master", bondName);
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(),
|
||||
String.format("将物理网卡 [%s] 加入 Bond [%s] 失败: %s", phyIf, bondName, e.getMessage()));
|
||||
}
|
||||
|
||||
newlyAddedSlaveNames.add(slaveConnectionName);
|
||||
}
|
||||
|
||||
try {
|
||||
executeCommand("nmcli", "con", "up", bondName);
|
||||
//激活后删除
|
||||
for (String uuid : uuidsToDown) {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "delete", "uuid", uuid);
|
||||
} catch (Exception e) {
|
||||
log.warn("Bond配置已生效,但清理废弃的旧连接 [{}] 失败: {}", uuid, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "激活 Bond [" + bondName + "] 失败: " + e.getMessage());
|
||||
}
|
||||
log.error("将网卡加入 Bond 失败,触发回滚,清理刚创建的 slave 连接", e);
|
||||
|
||||
return String.format("Bond [%s] 成功添加 %d 个从属网卡并已激活", bondName, slaves.size());
|
||||
for (String addedSlaveName : newlyAddedSlaveNames) {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "delete", addedSlaveName);
|
||||
} catch (Exception rollbackEx) {
|
||||
log.error("回滚失败: 无法删除的从属连接 [{}]", addedSlaveName, rollbackEx);
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "将网卡加入 Bond 失败,并且无法删除刚创建的从属连接");
|
||||
}
|
||||
}
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "添加物理网卡到 Bond 失败,已清理刚创建的配置");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String removeSlaveFromBond(BondRemoveSlaveRequest req) {
|
||||
String bondName = req.getBondName();
|
||||
String bondName = trim(req.getBondName());
|
||||
List<String> slaves = req.getSlaveList();
|
||||
|
||||
if (bondName == null || bondName.trim().isEmpty()) {
|
||||
if (bondName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
if (slaves == null || slaves.isEmpty()) {
|
||||
throw new IllegalArgumentException("物理网卡名称不能为空");
|
||||
}
|
||||
|
||||
int deletedCount = 0;
|
||||
List<String> notFoundSlaves = new ArrayList<>();
|
||||
// 请求输出格式为: UUID:DEVICE:NAME
|
||||
List<String> lines;
|
||||
try {
|
||||
lines = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME", "con", "show");
|
||||
lines = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME,TYPE", "con", "show");
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "获取系统网络连接列表失败");
|
||||
}
|
||||
|
||||
int deletedCount = 0;
|
||||
|
||||
for (String phyIf : slaves) {
|
||||
if (phyIf == null || phyIf.trim().isEmpty()) continue;
|
||||
|
||||
String targetUuid = null;
|
||||
String slaveName = bondName + "-slave-" + phyIf;
|
||||
|
||||
@ -515,7 +661,7 @@ public class NetworkConfigService {
|
||||
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
|
||||
if (parts.length >= 3) {
|
||||
if (parts.length >= 4) {
|
||||
String uuid = parts[0];
|
||||
String device = parts[1].replace("\\:", ":");
|
||||
String name = parts[2].replace("\\:", ":");
|
||||
@ -529,83 +675,83 @@ public class NetworkConfigService {
|
||||
break;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
log.warn("查询网卡 [{}] master 属性失败,可能已被移除或状态异常", name);
|
||||
log.warn("查询网卡 [{}] master 属性失败", name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (targetUuid == null) {
|
||||
log.warn("未找到物理网卡 [{}] 对应的从属连接", phyIf);
|
||||
notFoundSlaves.add(phyIf);
|
||||
continue;
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "未找到网卡 [" + phyIf + "] 对应的从属配置,移除操作已中断");
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "down", "uuid", targetUuid);
|
||||
} catch (Exception e) {
|
||||
log.warn("停用从属网卡连接 [{}] 失败,忽略并继续删除: {}", targetUuid, e.getMessage());
|
||||
}
|
||||
|
||||
executeCommand("nmcli", "con", "delete", "uuid", targetUuid);
|
||||
deletedCount++;
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(),
|
||||
String.format("从 Bond [%s] 移除网卡 [%s] 失败。原因: %s",
|
||||
bondName, phyIf, e.getMessage()));
|
||||
log.error("移除网卡 [{}] 失败: {}", phyIf, e.getMessage());
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("从 Bond [%s] 移除网卡 [%s] 失败: %s", bondName, phyIf, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 更新Bond状态
|
||||
// 只要有成功删除的记录,就重新激活 Bond 使其生效
|
||||
if (deletedCount > 0) {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "up", bondName);
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(),"更新 Bond [" + bondName + "] 失败: " + e.getMessage());
|
||||
|
||||
}
|
||||
|
||||
if (notFoundSlaves.isEmpty()) {
|
||||
return String.format("Bond [%s] 删除 %d 个从属网卡并已激活", bondName, deletedCount);
|
||||
} else if (deletedCount == 0) {
|
||||
return String.format("Bond [%s] 激活成功,但请求的网卡 %s 均不存在于该 Bond 中", bondName, notFoundSlaves);
|
||||
} else {
|
||||
return String.format("Bond [%s] 删除 %d 个从属网卡并已激活。提示:网卡 %s 不存在,已被忽略",
|
||||
bondName, deletedCount, notFoundSlaves);
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "网卡移除成功,但重新激活 Bond [" + bondName + "] 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return String.format("成功从 Bond [%s] 中移除了 %d 个网卡", bondName, deletedCount);
|
||||
}
|
||||
|
||||
public List<String> getBondSlaves(String bondName) {
|
||||
if (bondName == null || bondName.trim().isEmpty()) {
|
||||
bondName = trim(bondName);
|
||||
if (bondName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
|
||||
List<String> slaveList = new ArrayList<>();
|
||||
List<String> lines;
|
||||
try {
|
||||
lines = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME", "con", "show");
|
||||
lines = executeCommand("nmcli", "-t", "-f", "UUID,DEVICE,NAME,TYPE", "con", "show");
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "查询 Bond 从属网卡列表失败");
|
||||
}
|
||||
|
||||
boolean bondExists = false;
|
||||
|
||||
for (String line : lines) {
|
||||
if (line.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = line.split("(?<!\\\\):", -1);
|
||||
if (parts.length >= 3) {
|
||||
if (parts.length >= 4) {
|
||||
String uuid = parts[0].replace("\\:", ":");
|
||||
String device = parts[1].replace("\\:", ":");
|
||||
String name = parts[2].replace("\\:", ":");
|
||||
String type = parts[3];
|
||||
|
||||
if (bondName.equals(name) && "bond".equals(type)) {
|
||||
bondExists = true;
|
||||
}
|
||||
|
||||
if (type != null && type.contains("ethernet")) {
|
||||
List<String> masterOutputs = executeCommand("nmcli", "-g", "connection.master", "con", "show", uuid);
|
||||
String master = masterOutputs.isEmpty() ? "" : masterOutputs.get(0).trim();
|
||||
|
||||
if (bondName.equals(master)) {
|
||||
if (!device.isEmpty() && !device.equals("--")) {
|
||||
// 网卡处于激活连接状态
|
||||
slaveList.add(device);
|
||||
} else {
|
||||
// 网卡处于断开状态
|
||||
String prefix = bondName + "-slave-";
|
||||
if (name.startsWith(prefix)) {
|
||||
slaveList.add(name.substring(prefix.length()));
|
||||
@ -614,6 +760,11 @@ public class NetworkConfigService {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bondExists) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Bond [" + bondName + "] 不存在");
|
||||
}
|
||||
|
||||
return slaveList;
|
||||
}
|
||||
@ -649,13 +800,14 @@ public class NetworkConfigService {
|
||||
}
|
||||
|
||||
|
||||
public String setBondMode(BondModifyModeRequest req) {
|
||||
public void setBondMode(BondModifyModeRequest req) {
|
||||
String bondName = req.getBondName();
|
||||
Integer mode = req.getMode();
|
||||
|
||||
if (bondName == null || bondName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Bond 名称不能为空");
|
||||
}
|
||||
bondName = bondName.trim();
|
||||
if (mode == null) {
|
||||
throw new IllegalArgumentException("新的 Bond 模式不能为空");
|
||||
}
|
||||
@ -687,7 +839,6 @@ public class NetworkConfigService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "重新激活 Bond [" + bondName + "] 失败" + e.getMessage());
|
||||
}
|
||||
|
||||
return String.format("Bond [%s] 已成功切换至 [%s] 模式并重启生效", bondName, newMode);
|
||||
}
|
||||
|
||||
|
||||
@ -737,11 +888,11 @@ public class NetworkConfigService {
|
||||
}
|
||||
|
||||
|
||||
public String setDefaultRoute(SetDefaultRouteRequest req) {
|
||||
public void setDefaultRoute(SetDefaultRouteRequest req) {
|
||||
String deviceName = req.getDeviceName();
|
||||
String gatewayIp = req.getGatewayIp();
|
||||
|
||||
// 1. 基础校验
|
||||
|
||||
if (deviceName == null || deviceName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("网卡名称不能为空");
|
||||
}
|
||||
@ -749,7 +900,11 @@ public class NetworkConfigService {
|
||||
throw new IllegalArgumentException("网关 IP 不能为空");
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
if (!IPV4_PATTERN.matcher(req.getGatewayIp().trim()).matches()) {
|
||||
throw new IllegalArgumentException("无效的网关地址格式: " + req.getGatewayIp());
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName().trim());
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
}
|
||||
@ -758,8 +913,9 @@ public class NetworkConfigService {
|
||||
try {
|
||||
executeCommand("nmcli", "con", "mod", connectionName,
|
||||
"ipv4.gateway", gatewayIp,
|
||||
//多张网卡配置了网关,需调整 Metric 优先级。
|
||||
"ipv4.route-metric", "50");
|
||||
//TODO待修改(多张网卡配置了网关,需调整 Metric 优先级)。
|
||||
"ipv4.route-metric", "50",
|
||||
"ipv4.never-default", "no");
|
||||
} catch (RuntimeException e) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("为网卡 [%s] 设置网关失败: %s", deviceName, e.getMessage()));
|
||||
}
|
||||
@ -772,27 +928,32 @@ public class NetworkConfigService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("网卡 [%s] 激活配置失败: %s", deviceName, e.getMessage()));
|
||||
}
|
||||
|
||||
return String.format("成功将网卡 [%s] 的网关设置为 [%s]", deviceName, gatewayIp);
|
||||
}
|
||||
|
||||
|
||||
public String addStaticRoute(AddStaticRouteRequest req) {
|
||||
public void addStaticRoute(AddStaticRouteRequest req) {
|
||||
String deviceName = req.getDeviceName();
|
||||
String targetCidr = req.getTargetCidr();
|
||||
String nextHop = req.getNextHop();
|
||||
|
||||
//todo 对gatewayIp、targetCidr、nextHop增加格式校验。可用正则表达式
|
||||
if (deviceName == null || deviceName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("网卡名称不能为空");
|
||||
}
|
||||
if (targetCidr == null || targetCidr.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("目标网段(CIDR)不能为空");
|
||||
}
|
||||
|
||||
if (!CIDR_PATTERN.matcher(targetCidr.trim()).matches()) {
|
||||
throw new IllegalArgumentException("非法的CIDR目标网段格式");
|
||||
}
|
||||
|
||||
if (nextHop != null && !IPV4_PATTERN.matcher(nextHop.trim()).matches()) {
|
||||
throw new IllegalArgumentException("非法的下一跳IP格式");
|
||||
}
|
||||
// if (nextHop == null || nextHop.trim().isEmpty()) {
|
||||
// throw new IllegalArgumentException("下一跳 IP 不能为空");
|
||||
// }
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName().trim());
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
}
|
||||
@ -819,18 +980,17 @@ public class NetworkConfigService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("网卡 [%s] 激活配置失败: %s", deviceName, e.getMessage()));
|
||||
}
|
||||
|
||||
return String.format("成功向网卡 [%s] 添加静态路由", deviceName);
|
||||
}
|
||||
|
||||
|
||||
public String deleteDefaultRoute(DeleteDefaultRouteRequest req) {
|
||||
public void deleteDefaultRoute(DeleteDefaultRouteRequest req) {
|
||||
String deviceName = req.getDeviceName();
|
||||
|
||||
if (deviceName == null || deviceName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("网卡名称不能为空");
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName().trim());
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
}
|
||||
@ -848,12 +1008,11 @@ public class NetworkConfigService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("网卡 [%s] 激活配置失败: %s", deviceName, e.getMessage()));
|
||||
}
|
||||
|
||||
return String.format("已成功删除网卡 [%s] 的默认路由", deviceName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String deleteStaticRoute(DeleteStaticRouteRequest req) {
|
||||
public void deleteStaticRoute(DeleteStaticRouteRequest req) {
|
||||
String deviceName = req.getDeviceName();
|
||||
String targetCidr = req.getTargetCidr();
|
||||
String nextHop = req.getNextHop();
|
||||
@ -868,6 +1027,14 @@ public class NetworkConfigService {
|
||||
// throw new IllegalArgumentException("下一跳 IP 不能为空");
|
||||
// }
|
||||
|
||||
if (!CIDR_PATTERN.matcher(targetCidr.trim()).matches()) {
|
||||
throw new IllegalArgumentException("非法的CIDR目标网段格式");
|
||||
}
|
||||
|
||||
if (nextHop != null && !IPV4_PATTERN.matcher(nextHop.trim()).matches()) {
|
||||
throw new IllegalArgumentException("非法的下一跳IP格式");
|
||||
}
|
||||
|
||||
String connectionName = getConnectionNameByDeviceName(req.getDeviceName());
|
||||
if (connectionName == null){
|
||||
connectionName = req.getDeviceName();
|
||||
@ -896,24 +1063,23 @@ public class NetworkConfigService {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), String.format("网卡 [%s] 激活配置失败: %s", deviceName, e.getMessage()));
|
||||
}
|
||||
|
||||
return String.format("成功从网卡 [%s] 删除静态路由", deviceName);
|
||||
}
|
||||
|
||||
|
||||
private boolean isValidIpv4(String ip) {
|
||||
if (ip == null || ip.isEmpty()) return false;
|
||||
try {
|
||||
ipToInt(ip);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// private boolean isValidIpv4(String ip) {
|
||||
// if (ip == null || ip.isEmpty()) return false;
|
||||
// try {
|
||||
// ipToInt(ip);
|
||||
// return true;
|
||||
// } catch (IllegalArgumentException e) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
private boolean isValidIpv6(String ip) {
|
||||
if (ip == null || ip.isEmpty()) return false;
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||
InetAddress inetAddress = InetAddress.getByName(ip.trim());
|
||||
return inetAddress instanceof Inet6Address;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
@ -925,10 +1091,13 @@ public class NetworkConfigService {
|
||||
if (parts.length != 4) {
|
||||
throw new IllegalArgumentException("非法的 IPv4 地址: " + ipv4);
|
||||
}
|
||||
//TODO 前导零处理
|
||||
int result = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int octet = Integer.parseInt(parts[i]);
|
||||
String part = parts[i];
|
||||
int octet = Integer.parseInt(part);
|
||||
if (part.length() > 1 && part.startsWith("0")) {
|
||||
throw new IllegalArgumentException("非法的 IPv4 地址,存在前导零: " + part);
|
||||
}
|
||||
if (octet < 0 || octet > 255) {
|
||||
throw new IllegalArgumentException("非法的 IPv4 地址段: " + octet);
|
||||
}
|
||||
@ -999,7 +1168,67 @@ public class NetworkConfigService {
|
||||
throw new RuntimeException("服务器内部处理中断");}
|
||||
}
|
||||
|
||||
|
||||
private static String trim(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//private void applyNmcliConnectionConfig(String deviceName, boolean isIpv6, String ipWithPrefix, String gateway) {
|
||||
// String protocol = isIpv6 ? "ipv6" : "ipv4";
|
||||
// boolean hasGateway = gateway != null && !gateway.trim().isEmpty();
|
||||
//
|
||||
// boolean isNewConnection = false;
|
||||
// String connectionName = getConnectionNameByDeviceName(deviceName);
|
||||
// if (connectionName == null) {
|
||||
// connectionName = deviceName;
|
||||
// isNewConnection = true;
|
||||
// }
|
||||
//
|
||||
// if (isNewConnection) {
|
||||
// // 新建连接
|
||||
// List<String> cmdArgs = new ArrayList<>(Arrays.asList(
|
||||
// "nmcli", "con", "add",
|
||||
// "type", "ethernet",
|
||||
// "con-name", connectionName,
|
||||
// "ifname", deviceName,
|
||||
// protocol + ".method", "manual",
|
||||
// protocol + ".addresses", ipWithPrefix
|
||||
// ));
|
||||
//
|
||||
// if (hasGateway) {
|
||||
// cmdArgs.add(protocol + ".gateway");
|
||||
// cmdArgs.add(gateway.trim());
|
||||
// // 设置了网关,但明确告诉系统:不要把它当默认路由!
|
||||
// cmdArgs.add(protocol + ".never-default");
|
||||
// cmdArgs.add("yes");
|
||||
// }
|
||||
// executeCommand(cmdArgs.toArray(new String[0]));
|
||||
//
|
||||
// } else {
|
||||
// // 修改连接
|
||||
// List<String> cmdArgs = new ArrayList<>(Arrays.asList(
|
||||
// "nmcli", "con", "mod", connectionName,
|
||||
// protocol + ".method", "manual",
|
||||
// protocol + ".addresses", ipWithPrefix
|
||||
// ));
|
||||
//
|
||||
// if (hasGateway) {
|
||||
// cmdArgs.add(protocol + ".gateway");
|
||||
// cmdArgs.add(gateway.trim());
|
||||
// // 修改时,同样加上这个限制
|
||||
// cmdArgs.add(protocol + ".never-default");
|
||||
// cmdArgs.add("yes");
|
||||
// } else {
|
||||
// // 如果没有网关,清空旧网关
|
||||
// cmdArgs.add(protocol + ".gateway");
|
||||
// cmdArgs.add("");
|
||||
// }
|
||||
//
|
||||
// executeCommand(cmdArgs.toArray(new String[0]));
|
||||
// }
|
||||
//
|
||||
// // 激活连接
|
||||
// executeCommand("nmcli", "con", "up", connectionName);
|
||||
//}
|
||||
|
||||
@ -1,18 +1,34 @@
|
||||
package com.cisd.tms.modules.device.service.impl;
|
||||
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.modules.device.dto.TimeConfigRequest;
|
||||
import com.cisd.tms.modules.device.service.TimeConfigService;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
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;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TimeConfigServiceImpl implements TimeConfigService {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm").withResolverStyle(ResolverStyle.STRICT);
|
||||
|
||||
@Override
|
||||
public void processTimeConfig(TimeConfigRequest request) {
|
||||
String mode = request.getMode();
|
||||
@ -25,15 +41,35 @@ public class TimeConfigServiceImpl implements TimeConfigService {
|
||||
}
|
||||
|
||||
String datetime = request.getDatetime();
|
||||
//todo 建议用LocalDateTime进行校验格式
|
||||
if (datetime == null || !datetime.matches("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$")) {
|
||||
if (datetime == null) {
|
||||
throw new IllegalArgumentException("Invalid or empty datetime format. Expected: YYYY-MM-DD HH:MM");
|
||||
}
|
||||
try {
|
||||
LocalDateTime.parse(datetime, FORMATTER);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new IllegalArgumentException("Invalid or empty datetime format. Expected: YYYY-MM-DD HH:MM", e);
|
||||
}
|
||||
|
||||
executeCommand("timedatectl set-timezone " + request.getTimezone().trim());
|
||||
executeCommand("systemctl stop chronyd");
|
||||
//todo 失败后重启chronyd
|
||||
executeCommand("date -s '" + request.getDatetime() + "'");
|
||||
try{
|
||||
executeCommand("timedatectl", "set-timezone", request.getTimezone().trim());
|
||||
} catch (Exception e){
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "设置时区失败");
|
||||
}
|
||||
|
||||
try {
|
||||
executeCommand("systemctl", "stop", "chronyd");
|
||||
} catch (Exception e){
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "停止chronyd服务失败");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
executeCommand("date", "-s", request.getDatetime());
|
||||
} catch (Exception e) {
|
||||
// 失败后重启 chronyd
|
||||
executeCommand("systemctl", "start", "chronyd");
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "设置时间失败,已回退并重启 chronyd 服务");
|
||||
}
|
||||
|
||||
} else if ("NTP".equalsIgnoreCase(mode)) {
|
||||
|
||||
@ -41,72 +77,112 @@ public class TimeConfigServiceImpl implements TimeConfigService {
|
||||
if (servers == null || servers.isEmpty()) {
|
||||
throw new IllegalArgumentException("At least one NTP server IP must be provided in NTP mode");
|
||||
}
|
||||
//todo 参数校验SyncInterval:限制正数、范围或 2 的幂
|
||||
int interval = request.getSyncInterval() != null ? request.getSyncInterval() : 600;
|
||||
int pollExp = (int) Math.round(Math.log(interval) / Math.log(2));
|
||||
|
||||
int interval = request.getSyncInterval() != null ? request.getSyncInterval() : 512;
|
||||
if (interval <= 0) {
|
||||
throw new IllegalArgumentException("SyncInterval must be a positive integer.");
|
||||
}
|
||||
if ((interval & (interval - 1)) != 0) {
|
||||
throw new IllegalArgumentException("SyncInterval must be a power of 2.");
|
||||
}
|
||||
int MIN_INTERVAL = 8;
|
||||
int MAX_INTERVAL = 131072;
|
||||
if (interval < MIN_INTERVAL || interval > MAX_INTERVAL) {
|
||||
throw new IllegalArgumentException(String.format("SyncInterval must be between %d and %d.", MIN_INTERVAL, MAX_INTERVAL));
|
||||
}
|
||||
|
||||
int pollExp = Integer.numberOfTrailingZeros(interval);
|
||||
|
||||
StringBuilder serverConfLines = new StringBuilder();
|
||||
for (String server : servers) {
|
||||
if (!server.matches("^[A-Za-z0-9.-]+$")) {
|
||||
throw new IllegalArgumentException("Invalid NTP server address format: " + server);
|
||||
}
|
||||
|
||||
String pingResult = executeCommand("ping -c 1 -W 1 " + server);
|
||||
String pingResult = executeCommand("ping", "-c", "1", "-W", "1", server);
|
||||
if (!pingResult.contains("1 received")) {
|
||||
throw new RuntimeException("NTP server is unreachable via ping: " + server);
|
||||
}
|
||||
serverConfLines.append("server ").append(server)
|
||||
.append(" iburst minpoll ").append(pollExp)
|
||||
.append(" maxpoll ").append(pollExp).append("\\n");
|
||||
.append(" maxpoll ").append(pollExp).append("\n");
|
||||
}
|
||||
String confContent = serverConfLines +
|
||||
"driftfile /var/lib/chrony/drift\\n" +
|
||||
"makestep 1.0 3\\n" +
|
||||
"rtcsync\\n";
|
||||
//todo 这里直接重写配置文件,如果失败则原配置丢失。建议备份,失败后回滚。
|
||||
executeCommand("echo -e '" + confContent + "' > /etc/chrony.conf");
|
||||
executeCommand("systemctl restart chronyd");
|
||||
executeCommand("chronyc -a makestep");
|
||||
String status = executeCommand("chronyc tracking");
|
||||
"driftfile /var/lib/chrony/drift\n" +
|
||||
"makestep 1.0 3\n" +
|
||||
"rtcsync\n";
|
||||
|
||||
Path confPath = Paths.get("/etc/chrony.conf");
|
||||
Path backupPath = Paths.get("/etc/chrony.conf.bak");
|
||||
try {
|
||||
if (Files.exists(confPath)) {
|
||||
Files.copy(confPath, backupPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
Files.writeString(confPath, confContent);
|
||||
|
||||
executeCommand("systemctl", "restart", "chronyd");
|
||||
executeCommand("chronyc", "-a", "makestep");
|
||||
|
||||
String status = executeCommand("chronyc", "tracking");
|
||||
if (!status.contains("Reference ID")) {
|
||||
throw new RuntimeException("NTP sync status check failed");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
if (Files.exists(backupPath)) {
|
||||
Files.copy(backupPath, confPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
executeCommand("systemctl", "restart", "chronyd");
|
||||
}
|
||||
} catch (Exception rollbackEx) {
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Failed to apply new Chrony configuration. CRITICAL: Rollback also failed!");
|
||||
}
|
||||
|
||||
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Failed to apply new Chrony configuration. Rolled back to original state.");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown mode: " + mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String executeCommand(String command) {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", command);
|
||||
private String executeCommand(String... commandArgs) {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(commandArgs);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
Process process = null;
|
||||
try {
|
||||
process = processBuilder.start();
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
outputBuilder.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
String outputStr = outputBuilder.toString().trim();
|
||||
|
||||
if (exitCode != 0) {
|
||||
throw new RuntimeException("command execution failed: [" + command + "] ");
|
||||
log.error("Command execution failed. Exit code: {}, Command: {}, Output: {}",
|
||||
exitCode, Arrays.toString(commandArgs), outputStr);
|
||||
throw new RuntimeException("系统底层操作执行失败");
|
||||
}
|
||||
return output.toString();
|
||||
return outputStr;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("I/O error executing command: [" + command + "]", e);
|
||||
log.error("I/O error executing command: {}", Arrays.toString(commandArgs), e);
|
||||
throw new RuntimeException("服务器内部操作异常", e);
|
||||
} catch (InterruptedException e) {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("process was interrupted while executing command: [" + command + "]", e);
|
||||
log.error("Process was interrupted while executing command: {}", Arrays.toString(commandArgs), e);
|
||||
throw new RuntimeException("服务器内部处理中断", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,3 +354,15 @@ VALUES
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
);
|
||||
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- IP白名单表
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS tms_access_whitelist (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
ip VARCHAR(32) NOT NULL,
|
||||
mask VARCHAR(10) NOT NULL,
|
||||
create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user