diff --git a/.gitignore b/.gitignore index e415227..b13f4c2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ mem.pprof # Go语言相关 *.o *.a -/pkg/ /vendor/ # IDE和编辑器文件 diff --git a/cmd/client/main.go b/cmd/client/main.go index d06f26e..cf2498b 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -196,7 +196,7 @@ func (c *Client) MonitorTestStatus(testID string) error { return case "integrity": if info, ok := update.Data.(map[string]interface{}); ok { - c.logger.Infof("数据完整性: 可用块: %.0f, 损坏块: %.0f, 丢失块: %.0f, 数据丢失: %.2f MB", + c.logger.Infof("数据完整性: 可用块: %.0f, 损坏块: %.0f, 没写入块: %.0f, 数据丢失: %.2f MB", info["available_blocks"], info["corrupted_blocks"], info["missing_blocks"], info["data_loss_mb"]) } } diff --git a/cmd/server/main.go b/cmd/server/main.go index 1a3a6bc..71e57a7 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -487,11 +487,12 @@ func StartServer(cfg *config.Config, runner *TestRunner, logger *logrus.Logger) // 保存完整性信息 runner.SaveIntegrityInfo(testID, integrityInfo) - // 发送完整性信息 + // 发送完整性信息 && BlocksMap 清理掉 + integrityInfo.BlocksMap = nil runner.sendIntegrityUpdate(testID, "数据完整性检查完成", integrityInfo) - logger.Infof("恢复测试完成: 丢失数据: %.2f MB", integrityInfo.DataLossMB) + logger.Infof("恢复测试完成: 没填充的数据: %.2f MB", integrityInfo.DataLossMB) } else { logger.Error("不是断电测试实例,无法执行数据完整性检查") runner.sendErrorUpdate(testID, "不是断电测试实例,无法执行数据完整性检查") diff --git a/internal/testcase/power_loss-test.go b/internal/testcase/power_loss-test.go index 0a50246..cb160c9 100644 --- a/internal/testcase/power_loss-test.go +++ b/internal/testcase/power_loss-test.go @@ -403,7 +403,7 @@ func (t *PowerLossTest) CheckIntegrity() *model.IntegrityInfo { t.integrityInfo.DataLossMB = utils.BytesToMB((t.integrityInfo.MissingBlocks + t.integrityInfo.CorruptedBlocks) * t.blockSize) t.integrityInfo.RecoverySuccess = t.integrityInfo.CorruptedBlocks == 0 && t.integrityInfo.MissingBlocks == 0 - t.setMessage(fmt.Sprintf("数据完整性检查完成: %d 个块正常, %d 个块丢失, %d 个块损坏", + t.setMessage(fmt.Sprintf("数据完整性检查完成: %d 个块正常, %d 个块没写入, %d 个块损坏", t.integrityInfo.AvailableBlocks, t.integrityInfo.MissingBlocks, t.integrityInfo.CorruptedBlocks)) return t.integrityInfo diff --git a/pkg/opencastools/opencas.go b/pkg/opencastools/opencas.go new file mode 100644 index 0000000..e9deedc --- /dev/null +++ b/pkg/opencastools/opencas.go @@ -0,0 +1,269 @@ +package opencastools + +import ( + "fmt" + "regexp" + "strings" + + "plp-test/internal/utils" + + "github.com/sirupsen/logrus" +) + +// CacheInstance 表示Open-CAS缓存实例 +type CacheInstance struct { + ID string + CacheDevice string + CoreDevice string + CacheMode string + Status string + WritePolicy string + Mounted bool + MountPoint string +} + +// OpenCASManager 管理Open-CAS实例 +type OpenCASManager struct { + logger *logrus.Logger +} + +// NewOpenCASManager 创建OpenCAS管理器 +func NewOpenCASManager(logger *logrus.Logger) *OpenCASManager { + return &OpenCASManager{ + logger: logger, + } +} + +// CreateCacheInstance 创建一个Open-CAS缓存实例 +func (m *OpenCASManager) CreateCacheInstance(id string, cacheDevice, coreDevice, cacheMode string) error { + m.logger.Infof("创建缓存实例 %s: 缓存设备=%s, 核心设备=%s, 模式=%s", id, cacheDevice, coreDevice, cacheMode) + + // 检查设备是否存在 + if !utils.FileExists(cacheDevice) { + return fmt.Errorf("缓存设备不存在: %s", cacheDevice) + } + if !utils.FileExists(coreDevice) { + return fmt.Errorf("核心设备不存在: %s", coreDevice) + } + + // 检查设备是否已被使用 + output, err := utils.ExecuteCommand("casadm", "-L") + if err == nil && strings.Contains(output, cacheDevice) { + return fmt.Errorf("缓存设备已在使用中: %s", cacheDevice) + } + + // 启动缓存 + _, err = utils.ExecuteCommand("casadm", "-S", "-d", cacheDevice, "-i", id, "-c", cacheMode) + if err != nil { + msg, err := utils.ExecuteCommand("casadm", "-S", "-d", cacheDevice, "--load") + if err != nil && !strings.Contains(msg, "is already used as cache") { + return fmt.Errorf("启动缓存失败: %v", err) + } + } + + // 添加核心设备 + msg, err := utils.ExecuteCommand("casadm", "-A", "-i", id, "-d", coreDevice) + if err != nil && !strings.Contains(msg, "Device already added as a core") { + // 如果添加核心设备失败,尝试停止缓存 + utils.ExecuteCommand("casadm", "-T", "-i", id) + return fmt.Errorf("添加核心设备失败: %v", err) + } + + m.logger.Infof("缓存实例 %s 创建成功", id) + return nil +} + +// StopCacheInstance 停止缓存实例 +func (m *OpenCASManager) StopCacheInstance(id string) error { + m.logger.Infof("停止缓存实例 %s", id) + _, err := utils.ExecuteCommand("casadm", "-T", "-i", id) + if err != nil { + return fmt.Errorf("停止缓存实例失败: %v", err) + } + return nil +} + +// GetCacheStatus 获取缓存状态 +func (m *OpenCASManager) GetCacheStatus(id string) (*CacheInstance, error) { + output, err := utils.ExecuteCommand("casadm", "-L") + if err != nil { + return nil, fmt.Errorf("获取缓存状态失败: %v", err) + } + + // 解析输出以查找指定ID的缓存实例 + lines := strings.Split(output, "\n") + var cacheInfo, coreInfo string + for i, line := range lines { + if strings.Contains(line, "cache "+id) { + if i+1 < len(lines) { + cacheInfo = lines[i+1] + } + } + if strings.Contains(line, "└─") && strings.Contains(line, "core") { + coreInfo = line + } + } + + if cacheInfo == "" { + return nil, fmt.Errorf("未找到ID为 %s 的缓存实例", id) + } + + // 提取缓存信息 + instance := &CacheInstance{ + ID: id, + } + + // 提取缓存设备 + cacheDevicePattern := regexp.MustCompile(`/dev/\w+`) + cacheMatches := cacheDevicePattern.FindStringSubmatch(cacheInfo) + if len(cacheMatches) > 0 { + instance.CacheDevice = cacheMatches[0] + } + + // 提取核心设备 + coreMatches := cacheDevicePattern.FindStringSubmatch(coreInfo) + if len(coreMatches) > 0 { + instance.CoreDevice = coreMatches[0] + } + + // 提取缓存模式 + if strings.Contains(cacheInfo, "wb") { + instance.CacheMode = "Write-Back" + instance.WritePolicy = "Write-Back" + } else if strings.Contains(cacheInfo, "wt") { + instance.CacheMode = "Write-Through" + instance.WritePolicy = "Write-Through" + } else if strings.Contains(cacheInfo, "wo") { + instance.CacheMode = "Write-Only" + instance.WritePolicy = "Write-Only" + } else if strings.Contains(cacheInfo, "pt") { + instance.CacheMode = "Pass-Through" + instance.WritePolicy = "Pass-Through" + } + + // 检查状态 + if strings.Contains(cacheInfo, "Running") { + instance.Status = "Running" + } else if strings.Contains(cacheInfo, "Stopping") { + instance.Status = "Stopping" + } else { + instance.Status = "Unknown" + } + + // 检查是否已挂载 + cacheDevicePath := fmt.Sprintf("/dev/cas%s-1", id) + instance.Mounted = utils.IsMounted(cacheDevicePath) + + // 如果已挂载,尝试获取挂载点 + if instance.Mounted { + output, err := utils.ExecuteCommand("mount") + if err == nil { + lines = strings.Split(output, "\n") + for _, line := range lines { + if strings.Contains(line, cacheDevicePath) { + parts := strings.Split(line, " ") + if len(parts) > 2 { + instance.MountPoint = parts[2] + } + } + } + } + } + + return instance, nil +} + +// FlushCache 刷新缓存数据 +func (m *OpenCASManager) FlushCache(id string) error { + m.logger.Infof("刷新缓存实例 %s 的数据", id) + _, err := utils.ExecuteCommand("casadm", "-F", "-i", id) + if err != nil { + return fmt.Errorf("刷新缓存失败: %v", err) + } + return nil +} + +// GetStats 获取缓存统计信息 +func (m *OpenCASManager) GetStats(id string) (string, error) { + output, err := utils.ExecuteCommand("casadm", "-P", "-i", id, "--stats") + if err != nil { + return "", fmt.Errorf("获取缓存统计信息失败: %v", err) + } + return output, nil +} + +// FormatDevice 格式化设备 +func (m *OpenCASManager) FormatDevice(device, fsType string) error { + m.logger.Infof("格式化设备 %s 为 %s 文件系统", device, fsType) + _, err := utils.ExecuteCommand("mkfs", "-t", fsType, device) + if err != nil { + return fmt.Errorf("格式化设备失败: %v", err) + } + return nil +} + +// MountDevice 挂载设备 +func (m *OpenCASManager) MountDevice(device, mountPoint string) error { + m.logger.Infof("挂载设备 %s 到 %s", device, mountPoint) + + // 确保挂载点存在 + err := utils.CreateDirIfNotExist(mountPoint) + if err != nil { + return fmt.Errorf("创建挂载点失败: %v", err) + } + + // 执行挂载 + _, err = utils.ExecuteCommand("mount", device, mountPoint) + if err != nil { + return fmt.Errorf("挂载设备失败: %v", err) + } + return nil +} + +// UnmountDevice 卸载设备 +func (m *OpenCASManager) UnmountDevice(mountPoint string) error { + m.logger.Infof("卸载挂载点 %s", mountPoint) + _, err := utils.ExecuteCommand("umount", mountPoint) + if err != nil { + return fmt.Errorf("卸载设备失败: %v", err) + } + return nil +} + +// SimulatePowerCut 模拟断电 +func (m *OpenCASManager) SimulatePowerCut(id string) error { + m.logger.Warn("模拟断电,将强制停止缓存实例 %s", id) + + // 获取缓存状态 + instance, err := m.GetCacheStatus(id) + if err != nil { + return fmt.Errorf("获取缓存状态失败: %v", err) + } + + // 如果已挂载,先卸载 + if instance.Mounted && instance.MountPoint != "" { + err = m.UnmountDevice(instance.MountPoint) + if err != nil { + m.logger.Warn("卸载设备失败,继续强制停止: %v", err) + } + } + + // 强制停止缓存,模拟断电 + _, err = utils.ExecuteCommand("casadm", "-T", "-i", id, "-f") + if err != nil { + return fmt.Errorf("模拟断电失败: %v", err) + } + + m.logger.Info("模拟断电完成") + return nil +} + +// RepairCache 尝试修复损坏的缓存 +func (m *OpenCASManager) RepairCache(id string, cacheDevice string) error { + m.logger.Info("尝试修复缓存实例 %s", id) + _, err := utils.ExecuteCommand("casadm", "-L", "-i", id, "-d", cacheDevice) + if err != nil { + return fmt.Errorf("修复缓存失败: %v", err) + } + return nil +}