This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package unraid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/itworx/pulse/internal/share"
|
||||
)
|
||||
|
||||
// ShareSource maps Unraid's bounded, read-only shares query. Share usage is reported
|
||||
// by Unraid in KiB; Pulse's domain contract consistently uses bytes. The API does not
|
||||
// expose a complete placement scan in this selection, so no placement is inferred.
|
||||
type ShareSource struct {
|
||||
Client *Client
|
||||
Now func() time.Time
|
||||
}
|
||||
|
||||
func (s ShareSource) Snapshot(ctx context.Context) (share.RawSnapshot, error) {
|
||||
if s.Client == nil {
|
||||
return share.RawSnapshot{}, errors.New("Unraid client is required")
|
||||
}
|
||||
payload, err := s.Client.Query(ctx, "shares")
|
||||
if err != nil {
|
||||
return share.RawSnapshot{}, err
|
||||
}
|
||||
var response struct {
|
||||
Shares []struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Used json.RawMessage `json:"used"`
|
||||
Size json.RawMessage `json:"size"`
|
||||
Include []string `json:"include"`
|
||||
Exclude []string `json:"exclude"`
|
||||
Cache bool `json:"cache"`
|
||||
Allocator string `json:"allocator"`
|
||||
} `json:"shares"`
|
||||
}
|
||||
if err := json.Unmarshal(payload, &response); err != nil {
|
||||
return share.RawSnapshot{}, errors.New("decode Unraid shares response")
|
||||
}
|
||||
if len(response.Shares) > 1000 {
|
||||
return share.RawSnapshot{}, errors.New("Unraid share response exceeds bounds")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
if s.Now != nil {
|
||||
now = s.Now().UTC()
|
||||
}
|
||||
items := make([]share.RawShare, 0, len(response.Shares))
|
||||
for _, item := range response.Shares {
|
||||
used, err := rawUint(item.Used)
|
||||
if err != nil || strings.TrimSpace(item.ID) == "" || strings.TrimSpace(item.Name) == "" {
|
||||
return share.RawSnapshot{}, errors.New("Unraid share identity or usage is invalid")
|
||||
}
|
||||
// `cache` is a boolean in this API version. Preserve exactly that fact instead
|
||||
// of inventing a legacy mover policy or a primary/secondary placement.
|
||||
cachePolicy := "disabled"
|
||||
if item.Cache {
|
||||
cachePolicy = "enabled"
|
||||
}
|
||||
items = append(items, share.RawShare{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
StoragePolicy: share.StoragePolicy{Allocation: strings.TrimSpace(item.Allocator), CachePolicy: cachePolicy},
|
||||
UsedBytes: kilobytes(used),
|
||||
SizeObservedAt: now,
|
||||
SizeState: share.SizeAvailable,
|
||||
})
|
||||
}
|
||||
return share.RawSnapshot{Source: share.Source{ID: "unraid", Type: "unraid"}, Shares: items, ObservedAt: now, ReceivedAt: now}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user