ipmi fru: add Asset Tag and vendor Extra field write support (in-band)

Product Asset Tag (p 5) and the repeated custom "Extra" fields (Product
Extra p 7, Board Extra b 5/6/7, Chassis Extra c 2/3) from the Inspur FRU
field doc weren't writable — ipmitool prints identically-named lines for
each custom field with no index of its own, so a plain name lookup
couldn't tell them apart. parseFRUOutput now counts occurrences per area
to recover the real index, and the existing area/index round-trip in the
FRU editor write path picks it up automatically. Out-of-band (-H/-U/-P)
writing remains out of scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-07-01 17:21:26 +03:00
parent 2a7d366e50
commit 796acdfec1
2 changed files with 96 additions and 24 deletions
+37 -24
View File
@@ -37,26 +37,41 @@ var fruEditableFields = map[string]struct {
"Chassis Part Number": {"c", 0},
"Chassis Serial Number": {"c", 1},
"Chassis Serial": {"c", 1},
"Chassis Extra": {"c", 2},
// Board — vendor doc names and ipmitool abbreviated names
"Board Manufacturer": {"b", 0},
"Board Mfg": {"b", 0},
"Board Product Name": {"b", 1},
"Board Product": {"b", 1},
"Board Manufacturer": {"b", 0},
"Board Mfg": {"b", 0},
"Board Product Name": {"b", 1},
"Board Product": {"b", 1},
"Board Serial Number": {"b", 2},
"Board Serial": {"b", 2},
"Board Part Number": {"b", 3},
"Board Serial": {"b", 2},
"Board Part Number": {"b", 3},
// Product — vendor doc names and ipmitool abbreviated names
"Product Manufacturer": {"p", 0},
"Product Name": {"p", 1},
"Product Part Number": {"p", 2},
"Product Version": {"p", 3},
"Product Manufacturer": {"p", 0},
"Product Name": {"p", 1},
"Product Part Number": {"p", 2},
"Product Version": {"p", 3},
"Product Serial Number": {"p", 4},
"Product Serial": {"p", 4},
"Product Serial": {"p", 4},
"Product Asset Tag": {"p", 5},
}
// fruExtraBaseIndex gives the starting ipmitool field index for each area's
// repeated "<Area> Extra" custom fields, per the vendor FRU field doc (Chassis
// extra fields start at 2, Board at 5, Product at 7). ipmitool fru print
// emits one identically-named line per custom field, so parseFRUOutput
// counts occurrences to recover the real index for each one.
var fruExtraBaseIndex = map[string]struct {
Area string
Base int
}{
"Chassis Extra": {"c", 2},
"Board Extra": {"b", 5},
"Product Extra": {"p", 7},
}
func parseFRUOutput(output string) []fruField {
var fields []fruField
extraSeen := map[string]int{}
for _, line := range strings.Split(output, "\n") {
// Lines look like: " Field Name : value"
trimmed := strings.TrimLeft(line, " \t")
@@ -64,33 +79,32 @@ func parseFRUOutput(output string) []fruField {
continue
}
colon := strings.Index(trimmed, " : ")
valueOffset := 3
if colon < 0 {
// try ": " with no leading space before colon
colon = strings.Index(trimmed, ": ")
valueOffset = 2
if colon < 0 {
continue
}
name := strings.TrimSpace(trimmed[:colon])
value := strings.TrimSpace(trimmed[colon+2:])
if name == "" {
continue
}
editable, area, idx := fruFieldMeta(name)
fields = append(fields, fruField{Name: name, Value: value, Editable: editable, Area: area, Index: idx})
continue
}
name := strings.TrimSpace(trimmed[:colon])
value := strings.TrimSpace(trimmed[colon+3:])
value := strings.TrimSpace(trimmed[colon+valueOffset:])
if name == "" {
continue
}
editable, area, idx := fruFieldMeta(name)
editable, area, idx := fruFieldMeta(name, extraSeen)
fields = append(fields, fruField{Name: name, Value: value, Editable: editable, Area: area, Index: idx})
}
return fields
}
func fruFieldMeta(name string) (editable bool, area string, index int) {
func fruFieldMeta(name string, extraSeen map[string]int) (editable bool, area string, index int) {
if e, ok := fruExtraBaseIndex[name]; ok {
idx := e.Base + extraSeen[name]
extraSeen[name]++
return true, e.Area, idx
}
if e, ok := fruEditableFields[name]; ok {
return true, e.Area, e.Index
}
@@ -201,4 +215,3 @@ func runIPMIFRUWriteTask(ctx context.Context, j *jobState, exportDir string, p t
}
return nil
}