This commit is contained in:
@@ -136,26 +136,184 @@ func (s *Service) DestroyPool(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ImportPool imports a ZFS pool
|
||||
func (s *Service) ImportPool(name string, options map[string]string) error {
|
||||
args := []string{"import"}
|
||||
|
||||
// Add options
|
||||
for k, v := range options {
|
||||
args = append(args, "-o", fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
args = append(args, name)
|
||||
_, err := s.execCommand(s.zpoolPath, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExportPool exports a ZFS pool
|
||||
func (s *Service) ExportPool(name string, force bool) error {
|
||||
args := []string{"export"}
|
||||
if force {
|
||||
args = append(args, "-f")
|
||||
}
|
||||
args = append(args, name)
|
||||
_, err := s.execCommand(s.zpoolPath, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListAvailablePools returns pools that can be imported
|
||||
func (s *Service) ListAvailablePools() ([]string, error) {
|
||||
output, err := s.execCommand(s.zpoolPath, "import")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pools []string
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// Parse pool name from output like "pool: tank"
|
||||
if strings.HasPrefix(line, "pool:") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
pools = append(pools, parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
// ScrubPool starts a scrub operation on a pool
|
||||
func (s *Service) ScrubPool(name string) error {
|
||||
_, err := s.execCommand(s.zpoolPath, "scrub", name)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetScrubStatus returns the current scrub status
|
||||
func (s *Service) GetScrubStatus(name string) (string, error) {
|
||||
output, err := s.execCommand(s.zpoolPath, "status", name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// ScrubStatus represents detailed scrub operation status
|
||||
type ScrubStatus struct {
|
||||
Status string `json:"status"` // idle, in_progress, completed, error
|
||||
Progress float64 `json:"progress"` // 0-100
|
||||
TimeElapsed string `json:"time_elapsed"` // e.g., "2h 15m"
|
||||
TimeRemain string `json:"time_remain"` // e.g., "30m"
|
||||
Speed string `json:"speed"` // e.g., "100M/s"
|
||||
Errors int `json:"errors"` // number of errors found
|
||||
Repaired int `json:"repaired"` // number of errors repaired
|
||||
LastScrub string `json:"last_scrub"` // timestamp of last completed scrub
|
||||
}
|
||||
|
||||
// GetScrubStatus returns detailed scrub status with progress
|
||||
func (s *Service) GetScrubStatus(name string) (*ScrubStatus, error) {
|
||||
status := &ScrubStatus{
|
||||
Status: "idle",
|
||||
}
|
||||
|
||||
if strings.Contains(output, "scrub in progress") {
|
||||
return "in_progress", nil
|
||||
// Get pool status
|
||||
output, err := s.execCommand(s.zpoolPath, "status", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.Contains(output, "scrub repaired") {
|
||||
return "completed", nil
|
||||
|
||||
// Parse scrub information
|
||||
lines := strings.Split(output, "\n")
|
||||
inScrubSection := false
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// Check if scrub is in progress
|
||||
if strings.Contains(line, "scrub in progress") {
|
||||
status.Status = "in_progress"
|
||||
inScrubSection = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if scrub completed
|
||||
if strings.Contains(line, "scrub repaired") || strings.Contains(line, "scrub completed") {
|
||||
status.Status = "completed"
|
||||
status.Progress = 100.0
|
||||
// Extract repair information
|
||||
if strings.Contains(line, "repaired") {
|
||||
// Try to extract number of repairs
|
||||
parts := strings.Fields(line)
|
||||
for i, part := range parts {
|
||||
if part == "repaired" && i > 0 {
|
||||
// Previous part might be the number
|
||||
if repaired, err := strconv.Atoi(parts[i-1]); err == nil {
|
||||
status.Repaired = repaired
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse progress percentage
|
||||
if strings.Contains(line, "%") && inScrubSection {
|
||||
// Extract percentage from line like "scan: 45.2% done"
|
||||
parts := strings.Fields(line)
|
||||
for _, part := range parts {
|
||||
if strings.HasSuffix(part, "%") {
|
||||
if pct, err := strconv.ParseFloat(strings.TrimSuffix(part, "%"), 64); err == nil {
|
||||
status.Progress = pct
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse time elapsed
|
||||
if strings.Contains(line, "elapsed") && inScrubSection {
|
||||
// Extract time like "elapsed: 2h15m"
|
||||
parts := strings.Fields(line)
|
||||
for i, part := range parts {
|
||||
if part == "elapsed:" && i+1 < len(parts) {
|
||||
status.TimeElapsed = parts[i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse time remaining
|
||||
if strings.Contains(line, "remaining") && inScrubSection {
|
||||
parts := strings.Fields(line)
|
||||
for i, part := range parts {
|
||||
if part == "remaining:" && i+1 < len(parts) {
|
||||
status.TimeRemain = parts[i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse speed
|
||||
if strings.Contains(line, "scan rate") && inScrubSection {
|
||||
parts := strings.Fields(line)
|
||||
for i, part := range parts {
|
||||
if part == "rate" && i+1 < len(parts) {
|
||||
status.Speed = parts[i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse errors
|
||||
if strings.Contains(line, "errors:") && inScrubSection {
|
||||
parts := strings.Fields(line)
|
||||
for i, part := range parts {
|
||||
if part == "errors:" && i+1 < len(parts) {
|
||||
if errs, err := strconv.Atoi(parts[i+1]); err == nil {
|
||||
status.Errors = errs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "idle", nil
|
||||
|
||||
// Get last scrub time from pool properties
|
||||
lastScrub, err := s.execCommand(s.zfsPath, "get", "-H", "-o", "value", "lastscrub", name)
|
||||
if err == nil && lastScrub != "-" && lastScrub != "" {
|
||||
status.LastScrub = strings.TrimSpace(lastScrub)
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// ListDatasets returns all datasets in a pool (or all if pool is empty)
|
||||
|
||||
Reference in New Issue
Block a user