diff --git a/internal/fleetdb/attributes.go b/internal/fleetdb/attributes.go index 410c37d..784a258 100644 --- a/internal/fleetdb/attributes.go +++ b/internal/fleetdb/attributes.go @@ -2,12 +2,19 @@ package internalfleetdb import ( "encoding/json" + "fmt" + "os" "github.com/metal-toolbox/component-inventory/pkg/api/constants" "github.com/metal-toolbox/component-inventory/pkg/api/types" fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1" ) +const ( + uefiVariablesKey = "uefi-variables" + ssMetadataAttributeFound = "__ss_found" +) + func deviceVendorAttributes(cid *types.ComponentInventoryDevice) (map[string]string, *fleetdb.Attributes, error) { deviceVendorData := map[string]string{ constants.ServerSerialAttributeKey: "unknown", @@ -50,3 +57,35 @@ func attributeByNamespace(ns string, attributes []fleetdb.Attributes) *fleetdb.A return nil } + +// mustFilterAssetMetadata processes the asset inventory metadata to filter out fields we'll turn into versioned attributes (e.g. UEFIVariables) +func mustFilterAssetMetadata(inventory map[string]string) json.RawMessage { + excludedKeys := map[string]struct{}{ + uefiVariablesKey: {}, + } + + filtered := make(map[string]string) + + for k, v := range inventory { + if _, ok := excludedKeys[k]; ok { + continue + } + filtered[k] = v + } + + byt, err := json.Marshal(filtered) + if err != nil { + panic("serializing metadata string map") + } + + return byt +} + +// serverBIOSConfigNS returns the namespace server bios configuration are stored in. +func serverBIOSConfigNS(appKind types.AppKind) string { + if biosConfigNS := os.Getenv("CIS_FLEETDB_BIOS_CONFIG_NS"); biosConfigNS != "" { + return biosConfigNS + } + + return fmt.Sprintf("%s.%s.bios_configuration", constants.FleetDBNSPrefix, appKind) +} diff --git a/internal/fleetdb/fleetdb.go b/internal/fleetdb/fleetdb.go index 05d42c7..de698d1 100644 --- a/internal/fleetdb/fleetdb.go +++ b/internal/fleetdb/fleetdb.go @@ -3,7 +3,6 @@ package internalfleetdb import ( "context" "encoding/json" - "fmt" "github.com/google/uuid" "github.com/metal-toolbox/component-inventory/internal/app" @@ -17,7 +16,8 @@ type Client interface { GetServer(context.Context, uuid.UUID) (*fleetdb.Server, *fleetdb.ServerResponse, error) GetComponents(context.Context, uuid.UUID, *fleetdb.PaginationParams) (fleetdb.ServerComponentSlice, *fleetdb.ServerResponse, error) UpdateAttributes(context.Context, *fleetdb.Server, *types.ComponentInventoryDevice, *zap.Logger) error - UpdateServerBIOSConfig() error + UpdateServerBIOSConfig(context.Context, *fleetdb.Server, *types.ComponentInventoryDevice, *zap.Logger) error + UpdateServerMetadataAttributes(context.Context, uuid.UUID, *types.ComponentInventoryDevice, *zap.Logger) error } // Creates a new Client, with reasonable defaults @@ -37,7 +37,8 @@ func NewFleetDBClient(cfg *app.Configuration) (Client, error) { } type fleetDBClient struct { - client *fleetdb.Client + client *fleetdb.Client + appKind types.AppKind } func (fc fleetDBClient) GetServer(ctx context.Context, id uuid.UUID) (*fleetdb.Server, *fleetdb.ServerResponse, error) { @@ -78,13 +79,12 @@ func createUpdateServerAttributes(ctx context.Context, c *fleetdb.Client, server return err } - updatedVendorData := existingVendorData var changes bool for key := range newVendorData { - if updatedVendorData[key] == "" || updatedVendorData[key] == "unknown" { + if existingVendorData[key] == "" || existingVendorData[key] == "unknown" { if newVendorData[key] != "unknown" { changes = true - updatedVendorData[key] = newVendorData[key] + existingVendorData[key] = newVendorData[key] } } } @@ -93,8 +93,8 @@ func createUpdateServerAttributes(ctx context.Context, c *fleetdb.Client, server return nil } - if len(updatedVendorData) > 0 { - updateBytes, err := json.Marshal(updatedVendorData) + if len(existingVendorData) > 0 { + updateBytes, err := json.Marshal(existingVendorData) if err != nil { return err } @@ -107,10 +107,56 @@ func createUpdateServerAttributes(ctx context.Context, c *fleetdb.Client, server return nil } -func (fc fleetDBClient) UpdateServerBIOSConfig() error { - return createUpdateServerBIOSConfig() +func (fc fleetDBClient) UpdateServerBIOSConfig(ctx context.Context, server *fleetdb.Server, dev *types.ComponentInventoryDevice, log *zap.Logger) error { + // Nothing to publish + if len(*dev.BiosCfg) == 0 { + log.Info("no bios configuration collected") + return nil + } + + // marshal metadata from device + bc, err := json.Marshal(dev.BiosCfg) + if err != nil { + log.Error("invalid biosConfig") + return err + } + + va := fleetdb.VersionedAttributes{ + Namespace: serverBIOSConfigNS(fc.appKind), + Data: bc, + } + + _, err = fc.client.CreateVersionedAttributes(ctx, server.UUID, va) + + return err } -func createUpdateServerBIOSConfig() error { - return fmt.Errorf("unimplemented") +func (fc fleetDBClient) UpdateServerMetadataAttributes(ctx context.Context, serverID uuid.UUID, dev *types.ComponentInventoryDevice, log *zap.Logger) error { + // no metadata reported in inventory from device + if dev.Inv == nil || len(dev.Inv.Metadata) == 0 { + // XXX: should delete the metadata on the server-service record! + return nil + } + + // marshal metadata from device + metadata := mustFilterAssetMetadata(dev.Inv.Metadata) + + attribute := fleetdb.Attributes{ + Namespace: constants.ServerMetadataAttributeNS, + Data: metadata, + } + + // XXX: This would be much easier if serverservice/fleetdb supported upsert + // current asset metadata has no attributes set and no metadata attribute, create one + if _, ok := dev.Inv.Metadata[ssMetadataAttributeFound]; !ok { + _, err := fc.client.CreateAttributes(ctx, serverID, attribute) + log.Info("creating server attributes") + return err + } + + // update vendor, model attributes + _, err := fc.client.UpdateAttributes(ctx, serverID, constants.ServerMetadataAttributeNS, metadata) + + return err + } diff --git a/pkg/api/constants/constants.go b/pkg/api/constants/constants.go index 71ef474..ff2825e 100644 --- a/pkg/api/constants/constants.go +++ b/pkg/api/constants/constants.go @@ -13,17 +13,17 @@ const ( // server server service BMC address attribute key found under the bmcAttributeNamespace BmcIPAddressAttributeKey = "address" - // serverservice namespace prefix the data is stored in. - ServerServiceNSPrefix = "sh.hollow.alloy" + // fleetdb namespace prefix the data is stored in. + FleetDBNSPrefix = "sh.hollow.alloy" // server vendor, model attributes are stored in this namespace. - ServerVendorAttributeNS = ServerServiceNSPrefix + ".server_vendor_attributes" + ServerVendorAttributeNS = FleetDBNSPrefix + ".server_vendor_attributes" // additional server metadata are stored in this namespace. - ServerMetadataAttributeNS = ServerServiceNSPrefix + ".server_metadata_attributes" + ServerMetadataAttributeNS = FleetDBNSPrefix + ".server_metadata_attributes" // errors that occurred when connecting/collecting inventory from the bmc are stored here. - ServerBMCErrorsAttributeNS = ServerServiceNSPrefix + ".server_bmc_errors" + ServerBMCErrorsAttributeNS = FleetDBNSPrefix + ".server_bmc_errors" // server service server serial attribute key ServerSerialAttributeKey = "serial" diff --git a/pkg/api/routes/inventory.go b/pkg/api/routes/inventory.go index 91023c7..84e0ecd 100644 --- a/pkg/api/routes/inventory.go +++ b/pkg/api/routes/inventory.go @@ -12,9 +12,21 @@ import ( func processInband(ctx context.Context, c internalfleetdb.Client, server *fleetdb.Server, dev *types.ComponentInventoryDevice, log *zap.Logger) error { //nolint log.Info("processing", zap.String("server", server.Name), zap.String("device", dev.Inv.Serial)) + // update bisconfig + if err := c.UpdateServerBIOSConfig(ctx, server, dev, log); err != nil { + return err + } + + // create/update server serial, vendor, model attributes if err := c.UpdateAttributes(ctx, server, dev, log); err != nil { return err } + + // create update server metadata attributes + if err := c.UpdateServerMetadataAttributes(ctx, server.UUID, dev, log); err != nil { + return err + } + return errors.New("not implemented") } diff --git a/pkg/api/types/types.go b/pkg/api/types/types.go index c1fa55b..b016614 100644 --- a/pkg/api/types/types.go +++ b/pkg/api/types/types.go @@ -4,7 +4,10 @@ import ( "github.com/bmc-toolbox/common" ) -type BiosConfig map[string]string +type ( + BiosConfig map[string]string + AppKind string +) type ComponentInventoryDevice struct { ID string `json:"id,omitempty"`