Skip to content

Commit ed6510a

Browse files
committed
store/serverservice: fixes duplicate attribute errors and enriches NIC port data
Each component can include attributes, versioned attributes with a unique namespace value, if they are duplicated, serverservice will not accept the data. This fixes the error when publishing to serverservice ``` Server Component create/update error: CreateComponents: hollow client received a server error - response code: 400, message: , details: failed to insert into foreign table: models: unable to insert into attributes: pq: duplicate key value violates unique constraint \"idx_server_component_namespace\": error in server service API query: error in server service API query ``` Along with the fix, the NIC Port data has been enriched to include its own status attribute. ``` "versioned_attributes": [ { "namespace": "sh.hollow.alloy.outofband.status", "data": [ { "firmware": { "installed": "20.0.17" }, "status": { "Health": "OK", "State": "Enabled" } }, { "nic_port_status": { "Health": "OK", "State": "Enabled", "active_link_technology": "Ethernet", "id": "NIC.Slot.3-1-1", "link_status": "Up", "macaddress": "6C:FE:54:0F:21:40" } }, ... ```
1 parent 6012ef5 commit ed6510a

File tree

1 file changed

+187
-48
lines changed

1 file changed

+187
-48
lines changed

internal/store/serverservice/components.go

Lines changed: 187 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package serverservice
22

33
import (
4-
"context"
54
"encoding/json"
65
"fmt"
76
"strconv"
@@ -505,32 +504,53 @@ func (r *Store) nics(deviceVendor string, nics []*common.NIC) []*serverserviceap
505504
return nil
506505
}
507506

507+
// NIC port attributes go in here
508+
nicPortAttrs := []*attributes{}
509+
510+
// include NIC firmware attributes
511+
//
512+
// NIC port attributes are populated below.
513+
versionedAttrs := []*versionedAttributes{
514+
{
515+
Firmware: c.Firmware,
516+
Status: c.Status,
517+
},
518+
}
519+
508520
// TODO: fix up duplicate NIC attribute being dropped
509521
for _, p := range c.NICPorts {
510-
r.setAttributes(
511-
sc,
512-
&attributes{
513-
Description: c.Description,
514-
ProductName: c.ProductName,
515-
Oem: c.Oem,
516-
Metadata: c.Metadata,
517-
PhysicalID: p.PhysicalID,
518-
BusInfo: p.BusInfo,
519-
MacAddress: p.MacAddress,
520-
SpeedBits: p.SpeedBits,
521-
Capabilities: c.Capabilities,
522+
nicPortAttrs = append(nicPortAttrs, &attributes{
523+
ID: p.ID,
524+
Description: c.Description,
525+
ProductName: c.ProductName,
526+
Oem: c.Oem,
527+
Metadata: c.Metadata,
528+
PhysicalID: p.PhysicalID,
529+
BusInfo: p.BusInfo,
530+
MacAddress: p.MacAddress,
531+
SpeedBits: p.SpeedBits,
532+
Capabilities: c.Capabilities,
533+
})
534+
535+
if p.Status == nil && p.LinkStatus == "" && p.ActiveLinkTechnology == "" && !p.AutoNeg && p.MTUSize == 0 {
536+
continue
537+
}
538+
// Store the NIC Port status
539+
versionedAttrs = append(versionedAttrs, &versionedAttributes{
540+
NicPortStatus: &nicPortStatus{
541+
ID: p.ID,
542+
MTUSize: p.MTUSize,
543+
MacAddress: p.MacAddress,
544+
Status: p.Status,
545+
LinkStatus: p.LinkStatus,
546+
AutoSpeedNegotiation: p.AutoNeg,
547+
ActiveLinkTechnology: p.ActiveLinkTechnology,
522548
},
523-
)
549+
})
524550
}
525551

526-
r.setVersionedAttributes(
527-
deviceVendor,
528-
sc,
529-
&versionedAttributes{
530-
Firmware: c.Firmware,
531-
Status: c.Status,
532-
},
533-
)
552+
r.setAttributesList(sc, nicPortAttrs)
553+
r.setVersionedAttributesList(deviceVendor, sc, versionedAttrs)
534554

535555
components = append(components, sc)
536556
}
@@ -804,13 +824,74 @@ type attributes struct {
804824
Oem bool `json:"oem,omitempty"`
805825
}
806826

827+
// nicPortStatus holds the NIC port status which includes the health status and link status information.
828+
type nicPortStatus struct {
829+
*common.Status
830+
ID string `json:"id,omitempty"`
831+
MacAddress string `json:"macaddress,omitempty"`
832+
ActiveLinkTechnology string `json:"active_link_technology,omitempty"`
833+
LinkStatus string `json:"link_status,omitempty"`
834+
MTUSize int `json:"mtu_size,omitempty"`
835+
AutoSpeedNegotiation bool `json:"autospeednegotiation,omitempty"`
836+
}
837+
807838
// versionedAttributes are component attributes to be versioned in server service
808839
type versionedAttributes struct {
809-
Firmware *common.Firmware `json:"firmware,omitempty"`
810-
Status *common.Status `json:"status,omitempty"`
811-
UUID *uuid.UUID `json:"uuid,omitempty"` // UUID references firmware UUID identified in serverservice based on component/device attributes.
812-
SmartStatus string `json:"smart_status,omitempty"`
813-
Vendor string `json:"vendor,omitempty"`
840+
Firmware *common.Firmware `json:"firmware,omitempty"`
841+
Status *common.Status `json:"status,omitempty"`
842+
NicPortStatus *nicPortStatus `json:"nic_port_status,omitempty"`
843+
UUID *uuid.UUID `json:"uuid,omitempty"` // UUID references firmware UUID identified in serverservice based on component/device attributes.
844+
SmartStatus string `json:"smart_status,omitempty"`
845+
Vendor string `json:"vendor,omitempty"`
846+
}
847+
848+
// setAttributesList updates the given component with the given list of attributes.
849+
//
850+
// attributes per component in serverservice have a unique constraint on the component ID, namespace values.
851+
//
852+
// so if this method is called twice for the same component, namespace, that attribute will be ignored,
853+
func (r *Store) setAttributesList(component *serverserviceapi.ServerComponent, attrs []*attributes) {
854+
if len(attrs) == 0 {
855+
return
856+
}
857+
858+
// convert attributes to raw json
859+
data, err := json.Marshal(attrs)
860+
if err != nil {
861+
r.logger.WithFields(
862+
logrus.Fields{
863+
"slug": component.ComponentTypeSlug,
864+
"kind": fmt.Sprintf("%T", data),
865+
"err": err,
866+
}).Warn("error in conversion of versioned attributes to raw data")
867+
}
868+
869+
if component.Attributes == nil {
870+
component.Attributes = []serverserviceapi.Attributes{}
871+
} else {
872+
for _, existingA := range component.Attributes {
873+
if existingA.Namespace != r.attributeNS {
874+
continue
875+
}
876+
877+
r.logger.WithFields(
878+
logrus.Fields{
879+
"slug": component.ComponentTypeSlug,
880+
"kind": fmt.Sprintf("%T", data),
881+
"namespace": r.attributeNS,
882+
}).Warn("duplicate attribute list on component dropped - this is unexpected.")
883+
884+
return
885+
}
886+
}
887+
888+
component.Attributes = append(
889+
component.Attributes,
890+
serverserviceapi.Attributes{
891+
Namespace: r.attributeNS,
892+
Data: data,
893+
},
894+
)
814895
}
815896

816897
// setAttributes updates the serverservice API component object with the given attributes
@@ -845,7 +926,7 @@ func (r *Store) setAttributes(component *serverserviceapi.ServerComponent, attr
845926
"slug": component.ComponentTypeSlug,
846927
"kind": fmt.Sprintf("%T", data),
847928
"namespace": r.attributeNS,
848-
}).Warn("duplicate attribute on component dropped.")
929+
}).Warn("duplicate attribute on component dropped - use setAttributesList() instead.")
849930

850931
return
851932
}
@@ -860,25 +941,87 @@ func (r *Store) setAttributes(component *serverserviceapi.ServerComponent, attr
860941
)
861942
}
862943

863-
// setVersionedAttributes sets the given attributes on the serverservice server component.
944+
// setVersionedAttributesList updates the given component with given list of versioned attributes.
864945
//
865-
// note: versioned attributes has a constraint on the server_component_id, namespace
866-
func (r *Store) setVersionedAttributes(deviceVendor string, component *serverserviceapi.ServerComponent, vattr *versionedAttributes) {
867-
ctx := context.TODO()
946+
// versioned attributes per component in serverservice have a unique constraint on
947+
// the component ID, namespace, created_at values.
948+
// If this method is called twice for the same component, namespace, that versioned attribute will be ignored.
949+
func (r *Store) setVersionedAttributesList(deviceVendor string, component *serverserviceapi.ServerComponent, vattrs []*versionedAttributes) {
950+
if len(vattrs) == 0 {
951+
return
952+
}
868953

869-
// add FirmwareData
870-
if vattr.Firmware != nil {
871-
var err error
954+
// enrich firmware data
955+
for _, vattr := range vattrs {
956+
if vattr.Firmware == nil {
957+
continue
958+
}
959+
960+
r.enrichFirmwareData(deviceVendor, component.Vendor, vattr)
961+
}
962+
963+
// convert versioned attributes to raw json
964+
data, err := json.Marshal(vattrs)
965+
if err != nil {
966+
r.logger.WithFields(
967+
logrus.Fields{
968+
"slug": component.ComponentTypeSlug,
969+
"kind": fmt.Sprintf("%T", data),
970+
"err": err,
971+
}).Warn("error in conversion of versioned attributes to raw data")
972+
}
973+
974+
// skip empty json data containing just the braces `{}`
975+
min := 2
976+
if len(data) == min {
977+
return
978+
}
979+
980+
if component.VersionedAttributes == nil {
981+
component.VersionedAttributes = []serverserviceapi.VersionedAttributes{}
982+
} else {
983+
// versioned attributes per component in serverservice have a unique constraint on
984+
// the component ID, namespace, created_at values.
985+
//
986+
// so here we ignore the new attribute with the same namespace, if one already exists.
987+
for _, existingVA := range component.VersionedAttributes {
988+
if existingVA.Namespace != r.versionedAttributeNS {
989+
continue
990+
}
872991

873-
vattr, err = r.addFirmwareData(ctx, deviceVendor, component, vattr)
874-
if err != nil {
875992
r.logger.WithFields(
876993
logrus.Fields{
877-
"err": err,
878-
}).Warn("error adding firmware data to versioned attribute")
994+
"slug": component.ComponentTypeSlug,
995+
"kind": fmt.Sprintf("%T", data),
996+
"namespace": r.versionedAttributeNS,
997+
}).Warn("duplicate versioned attribute on component dropped - this was unexpected.")
998+
999+
return
8791000
}
8801001
}
8811002

1003+
component.VersionedAttributes = append(
1004+
component.VersionedAttributes,
1005+
serverserviceapi.VersionedAttributes{
1006+
Namespace: r.versionedAttributeNS,
1007+
Data: data,
1008+
},
1009+
)
1010+
}
1011+
1012+
// setVersionedAttributes updates a component with single versioned attribute.
1013+
//
1014+
// versioned attributes per component in serverservice have a unique constraint on
1015+
// the component ID, namespace, created_at values.
1016+
//
1017+
// so if this method is called twice for the same component, namespace, that versioned attribute will be ignored,
1018+
// the caller should invoke setVersionedAttributesList() instead.
1019+
func (r *Store) setVersionedAttributes(deviceVendor string, component *serverserviceapi.ServerComponent, vattr *versionedAttributes) {
1020+
// add FirmwareData
1021+
if vattr.Firmware != nil {
1022+
r.enrichFirmwareData(deviceVendor, component.Vendor, vattr)
1023+
}
1024+
8821025
// convert versioned attributes to raw json
8831026
data, err := json.Marshal(vattr)
8841027
if err != nil {
@@ -924,26 +1067,22 @@ func (r *Store) setVersionedAttributes(deviceVendor string, component *serverser
9241067
)
9251068
}
9261069

927-
// addFirmwareData queries ServerService for the firmware version and try to find a matcr.
928-
func (r *Store) addFirmwareData(ctx context.Context, deviceVendor string, component *serverserviceapi.ServerComponent, vattr *versionedAttributes) (vatrr *versionedAttributes, err error) {
1070+
// enrichFirmwareData queries ServerService for the firmware version and try to find a match.
1071+
//
1072+
// the given versionedAttribute object is updated to include the firmware vendor and the serverservice firmware UUID.
1073+
func (r *Store) enrichFirmwareData(deviceVendor, componentVendor string, vattr *versionedAttributes) {
9291074
// Check in the cache if we have a match by vendor + version
930-
for _, fw := range r.firmwares[component.Vendor] {
1075+
for _, fw := range r.firmwares[componentVendor] {
9311076
if strings.EqualFold(fw.Version, vattr.Firmware.Installed) {
9321077
vattr.Vendor = fw.Vendor
9331078
vattr.UUID = &fw.UUID
934-
935-
return vattr, nil
9361079
}
9371080
}
9381081

9391082
for _, fw := range r.firmwares[deviceVendor] {
9401083
if strings.EqualFold(fw.Version, vattr.Firmware.Installed) {
9411084
vattr.Vendor = fw.Vendor
9421085
vattr.UUID = &fw.UUID
943-
944-
return vattr, nil
9451086
}
9461087
}
947-
948-
return vattr, nil
9491088
}

0 commit comments

Comments
 (0)