feat(vm): 添加虚拟机快照功能

- 新增快照列表、创建快照、删除快照和还原快照的接口和实现
- 增加与外部 API 的交互,实现快照相关操作
- 新增快照操作相关的请求和响应模型
master
chenhao 2025-09-10 09:06:00 +08:00
parent 8856817b64
commit 5511401e40
7 changed files with 148 additions and 3 deletions

View File

@ -3,8 +3,9 @@ package com.unisinsight.project.controller;
import cn.hutool.json.JSONUtil;
import com.unisinsight.project.entity.req.DeleteIdReq;
import com.unisinsight.project.entity.req.ImageDesktopReq;
import com.unisinsight.project.entity.req.ImageVirtualMachinesReq;
import com.unisinsight.project.entity.req.SnapShotCreateReq;
import com.unisinsight.project.entity.req.SnapShotReq;
import com.unisinsight.project.entity.res.ImageVirtualMachinesRes;
import com.unisinsight.project.entity.res.PageResult;
import com.unisinsight.project.exception.BaseErrorCode;
@ -158,6 +159,34 @@ public class ImageVirtualMachinesController {
log.info("获取VNC信息请求参数为{}", vmName);
return service.getVncData(vmName);
}
@ApiOperation(value = "获取快照列表")
@GetMapping("/snapshot/list/{vmName}")
public Result<?> snapshotList(@PathVariable("vmName") String vmName) {
log.info("获取快照列表请求参数为:{}", vmName);
return service.snapshotList(vmName);
}
@ApiOperation(value = "创建快照")
@PostMapping("/snapshot/create")
public Result<?> createSnapshot(@RequestBody SnapShotCreateReq snapShotCreateReq) {
log.info("创建快照请求参数为:{}", snapShotCreateReq);
return service.createSnapshot(snapShotCreateReq);
}
@ApiOperation(value = "删除快照")
@PostMapping("/snapshot/delete")
public Result<?> deleteSnapshot(@RequestBody SnapShotReq snapShotCreateReq) {
log.info("删除快照请求参数为:{}", snapShotCreateReq);
return service.deleteSnapshot(snapShotCreateReq);
}
@ApiOperation(value = "还原快照")
@GetMapping("/snapshot/revert")
public Result<?> revertSnapshot(@RequestBody SnapShotReq snapShotCreateReq) {
log.info("还原快照请求参数为:{}", snapShotCreateReq);
return service.revertSnapshot(snapShotCreateReq);
}
}

View File

@ -0,0 +1,26 @@
package com.unisinsight.project.entity.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author : ch
* @version : 1.0
* @ClassName : SnapShotCreateReq
* @Description :
* @DATE : Created in 16:48 2025/9/9
* <pre> Copyright: Copyright(c) 2025 </pre>
* <pre> Company : </pre>
* Modification History:
* Date Author Version Discription
* --------------------------------------------------------------------------
* 2025/09/09 ch 1.0 Why & What is modified: <> *
*/
@Data
public class SnapShotCreateReq {
@JsonProperty("vm_name")
private String vmName;
private String name;
private String description;
}

View File

@ -0,0 +1,26 @@
package com.unisinsight.project.entity.req;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author : ch
* @version : 1.0
* @ClassName : SnapShotCreateReq
* @Description :
* @DATE : Created in 16:48 2025/9/9
* <pre> Copyright: Copyright(c) 2025 </pre>
* <pre> Company : </pre>
* Modification History:
* Date Author Version Discription
* --------------------------------------------------------------------------
* 2025/09/09 ch 1.0 Why & What is modified: <> *
*/
@Data
public class SnapShotReq {
@JsonProperty("vm_name")
private String vmName;
@JsonProperty("snapshot_name")
private String snapshotName;
}

View File

@ -93,5 +93,17 @@ public interface ExternalApiClient {
ApiResponse<List<BridgeInterfaceDTO>> getBridgeInterfaces();
@GetMapping("/api/v1/vm/{vmName}/vnc")
ApiResponse<VncData> getVncData(@PathVariable("vmName") String vmName);
@GetMapping("/api/v1/snapshot/list/{vm_name}")
ApiResponse<SnapshotsResponseDTO> snapshotList(@PathVariable("vm_name") String vmName);
@PostMapping("/api/v1/snapshot/create/{vm_name}")
ApiResponse createSnapshot(@PathVariable("vm_name") String vmName,@RequestBody SnapShotCreateReq snapshotCreateReq);
@PostMapping("/api/v1/snapshot/revert/{vm_name}/{snapshot_name}")
ApiResponse revertSnapshot(@PathVariable("vm_name") String vmName,@PathVariable("snapshot_name") String snapshotName);
@DeleteMapping("/api/v1/snapshot/delete/{vm_name}/{snapshot_name}")
ApiResponse deleteSnapshot(@PathVariable("vm_name") String vmName,@PathVariable("snapshot_name") String snapshotName);
}

View File

@ -3,8 +3,9 @@ package com.unisinsight.project.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.unisinsight.project.entity.dao.ImageVirtualMachines;
import com.unisinsight.project.entity.req.DeleteIdReq;
import com.unisinsight.project.entity.req.ImageDesktopReq;
import com.unisinsight.project.entity.req.ImageVirtualMachinesReq;
import com.unisinsight.project.entity.req.SnapShotCreateReq;
import com.unisinsight.project.entity.req.SnapShotReq;
import com.unisinsight.project.entity.res.ImageVirtualMachinesRes;
import com.unisinsight.project.entity.res.PageResult;
import com.unisinsight.project.exception.Result;
@ -63,5 +64,13 @@ public interface ImageVirtualMachinesService extends IService<ImageVirtualMachin
Result<?> attachIso(ImageVirtualMachinesReq req);
Result<?> getVncData(String vmName);
Result<?> snapshotList(String vmName);
Result<?> createSnapshot(SnapShotCreateReq snapShotCreateReq);
Result<?> deleteSnapshot(SnapShotReq snapShotCreateReq);
Result<?> revertSnapshot(SnapShotReq snapShotCreateReq);
}

View File

@ -65,6 +65,9 @@ public class ClientServiceImpl implements ClientService {
if (StringUtils.isNotBlank(e.getDesktopName())) {
map.put("name", e.getDesktopName()+"."+e.getDesktopType());
}
if (StringUtils.isNotBlank(e.getCloneName())) {
map.put("clone_name", e.getCloneName());
}
if (StringUtils.isNotBlank(e.getFileSize())) {
map.put("file_size", StringUtils.isNotEmpty(e.getFileSize()) ? Long.parseLong(e.getFileSize()) : 0L);
}

View File

@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unisinsight.project.entity.dao.*;
import com.unisinsight.project.entity.dto.ApiResponse;
import com.unisinsight.project.entity.dto.SnapshotsResponseDTO;
import com.unisinsight.project.entity.dto.VmInfoDTO;
import com.unisinsight.project.entity.req.*;
import com.unisinsight.project.entity.res.ImageStatusRes;
@ -435,7 +436,7 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl<ImageVirtualMac
updateWrapper.set(ImageDesktop::getPublishTime, DateUtil.date());
//转化bt
String fileNamewithSuffixes = detFilePath.substring(detFilePath.lastIndexOf(fileName));
externalTorrentClient.startBt(detFilePath, "/var/lib/vdi/test/" + fileNamewithSuffixes + ".torrent");
externalTorrentClient.startBt(detFilePath, "/vms/iso/" + fileNamewithSuffixes + ".torrent");
updateWrapper.set(ImageDesktop::getBtPath, torrentUrl + "/api/vdi/file/down/" + fileNamewithSuffixes + ".torrent");
imageDesktopService.update(updateWrapper);
System.out.println("任务已完成,执行特有操作...");
@ -589,5 +590,44 @@ public class ImageVirtualMachinesServiceImpl extends ServiceImpl<ImageVirtualMac
return Result.errorResultMessage(BaseErrorCode.HTTP_ERROR_CODE_500, vncData.getMessage());
}
@Override
public Result<?> snapshotList(String vmName) {
ApiResponse<SnapshotsResponseDTO> response = externalApiClient.snapshotList(vmName);
if ( !"200".equals(response.getCode()) ||response.getData() == null) {
return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, response.getMessage());
}
PageResult<SnapshotsResponseDTO.SnapshotItem> pageResult = new PageResult<>();
SnapshotsResponseDTO data = response.getData();
pageResult.setPageNum(data.getPage());
pageResult.setTotal(data.getTotal());
pageResult.setPageSize(data.getPage_size());
pageResult.setData(data.getItems());
pageResult.setTotal(data.getTotal());
return Result.successResult(pageResult);
}
@Override
public Result<?> createSnapshot(SnapShotCreateReq snapShotCreateReq) {
externalApiClient.createSnapshot(snapShotCreateReq.getVmName(), snapShotCreateReq);
return Result.successResult();
}
@Override
public Result<?> deleteSnapshot(SnapShotReq snapShotCreateReq) {
if (StringUtils.isEmpty(snapShotCreateReq.getVmName())|| StringUtils.isEmpty(snapShotCreateReq.getSnapshotName())){
return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "参数错误");
}
ApiResponse apiResponse = externalApiClient.deleteSnapshot(snapShotCreateReq.getVmName(), snapShotCreateReq.getSnapshotName());
return Result.successResult(apiResponse.getData());
}
@Override
public Result<?> revertSnapshot(SnapShotReq snapShotCreateReq) {
if (StringUtils.isEmpty(snapShotCreateReq.getVmName())|| StringUtils.isEmpty(snapShotCreateReq.getSnapshotName())){
return Result.errorResult(BaseErrorCode.HTTP_ERROR_CODE_500, "参数错误");
}
ApiResponse apiResponse = externalApiClient.revertSnapshot(snapShotCreateReq.getVmName(), snapShotCreateReq.getSnapshotName());
return Result.successResult(apiResponse.getData());
}
}