package adapter import ( "context" "errors" "strings" "pve-confsync/internal/model" "pve-confsync/internal/pve" ) // DatacenterID is the id of the single datacenter options object. const DatacenterID = "datacenter" // pveDefaultMACPrefix is injected by PVE on read when mac_prefix is unset // (PROXMOX_OUI in pve-cluster DataCenterConfig.pm); it is not a real setting. const pveDefaultMACPrefix = "BC:24:11" // datacenter handles GET/PUT /cluster/options (datacenter.cfg). // GET returns several options parsed into objects/lists, PUT expects property strings: // the canonical form is the property string (lists joined with ';' like datacenter.cfg). type datacenter struct{} func (datacenter) Kind() model.Kind { return model.KindDatacenter } func (datacenter) SDN() bool { return false } func (datacenter) Singleton() bool { return true } func (datacenter) List(ctx context.Context, c *pve.Client, _ ReadMode, _ []model.Object) ([]model.Object, []string, error) { var raw map[string]any if err := c.Get(ctx, "cluster/options", nil, &raw); err != nil { return nil, nil, err } // allowed-tags: read-only, computed for the calling user. fields := normalizeAPI(raw, "allowed-tags") for k, v := range fields { // top level lists (registered-tags) are stored as "a;b;c" in datacenter.cfg if l, ok := v.([]string); ok { fields[k] = strings.Join(l, ";") } } if fields["description"] == "" { delete(fields, "description") } if s, _ := fields["mac_prefix"].(string); strings.EqualFold(s, pveDefaultMACPrefix) { delete(fields, "mac_prefix") } return []model.Object{{Kind: model.KindDatacenter, ID: DatacenterID, Fields: fields}}, nil, nil } // Fields synchronized by default; any other option is ignored unless the // configuration says "sync". var dcDefaultSync = map[string]bool{ "tag-style": true, "registered-tags": true, "user-tag-access": true, "next-id": true, } func (datacenter) DefaultPolicy(f string) Policy { if dcDefaultSync[f] { return Sync } return Ignore } // Identity of the cluster or possible secrets (http_proxy may hold credentials). func (datacenter) Forbidden(f string) bool { switch f { case "webauthn", "u2f", "http_proxy": return true } return false } func (datacenter) Canonicalize(map[string]any) {} func (datacenter) Required(model.Object) []string { return nil } func (datacenter) Immutable(string) bool { return false } func (datacenter) Create(context.Context, *pve.Client, model.Object, string) error { return errors.New("datacenter options cannot be created") } func (datacenter) Delete(context.Context, *pve.Client, model.Object, string) error { return errors.New("datacenter options cannot be deleted") } func (datacenter) Update(ctx context.Context, c *pve.Client, _ model.Object, set map[string]any, unset []string, _ string) error { flat := map[string]any{} for k, v := range set { if l, ok := v.([]string); ok { flat[k] = strings.Join(l, ";") } else { flat[k] = v } } return c.Put(ctx, "cluster/options", form(flat, unset, ""), nil) }