package collector import ( "strings" "git.mchus.pro/mchus/logpile/internal/models" ) func (r redfishSnapshotReader) enrichNICsFromNetworkInterfaces(nics *[]models.NetworkAdapter, systemPaths []string) { if nics == nil { return } bySlot := make(map[string]int, len(*nics)) for i, nic := range *nics { bySlot[strings.ToLower(strings.TrimSpace(nic.Slot))] = i } for _, systemPath := range systemPaths { ifaces, err := r.getCollectionMembers(joinPath(systemPath, "/NetworkInterfaces")) if err != nil || len(ifaces) == 0 { continue } for _, iface := range ifaces { slot := firstNonEmpty(asString(iface["Id"]), asString(iface["Name"])) if strings.TrimSpace(slot) == "" { continue } idx, ok := bySlot[strings.ToLower(strings.TrimSpace(slot))] if !ok { *nics = append(*nics, models.NetworkAdapter{ Slot: slot, Present: true, Model: firstNonEmpty(asString(iface["Model"]), asString(iface["Name"])), Status: mapStatus(iface["Status"]), }) idx = len(*nics) - 1 bySlot[strings.ToLower(strings.TrimSpace(slot))] = idx } portsPath := redfishLinkedPath(iface, "NetworkPorts") if portsPath == "" { continue } portDocs, err := r.getCollectionMembers(portsPath) if err != nil || len(portDocs) == 0 { continue } macs := append([]string{}, (*nics)[idx].MACAddresses...) for _, p := range portDocs { macs = append(macs, collectNetworkPortMACs(p)...) } (*nics)[idx].MACAddresses = dedupeStrings(macs) if sanitizeNetworkPortCount((*nics)[idx].PortCount) == 0 { (*nics)[idx].PortCount = len(portDocs) } } } } func (r redfishSnapshotReader) collectNICs(chassisPaths []string) []models.NetworkAdapter { var nics []models.NetworkAdapter for _, chassisPath := range chassisPaths { adapterDocs, err := r.getCollectionMembers(joinPath(chassisPath, "/NetworkAdapters")) if err != nil { continue } for _, doc := range adapterDocs { nic := parseNIC(doc) for _, pciePath := range networkAdapterPCIeDevicePaths(doc) { pcieDoc, err := r.getJSON(pciePath) if err != nil { continue } functionDocs := r.getLinkedPCIeFunctions(pcieDoc) supplementalDocs := r.getLinkedSupplementalDocs(pcieDoc, "EnvironmentMetrics", "Metrics") for _, fn := range functionDocs { supplementalDocs = append(supplementalDocs, r.getLinkedSupplementalDocs(fn, "EnvironmentMetrics", "Metrics")...) } enrichNICFromPCIe(&nic, pcieDoc, functionDocs, supplementalDocs) } if len(nic.MACAddresses) == 0 { r.enrichNICMACsFromNetworkDeviceFunctions(&nic, doc) } nics = append(nics, nic) } } return dedupeNetworkAdapters(nics) } func (r redfishSnapshotReader) collectPCIeDevices(systemPaths, chassisPaths []string) []models.PCIeDevice { collections := make([]string, 0, len(systemPaths)+len(chassisPaths)) for _, systemPath := range systemPaths { collections = append(collections, joinPath(systemPath, "/PCIeDevices")) } for _, chassisPath := range chassisPaths { collections = append(collections, joinPath(chassisPath, "/PCIeDevices")) } var out []models.PCIeDevice for _, collectionPath := range collections { memberDocs, err := r.getCollectionMembers(collectionPath) if err != nil || len(memberDocs) == 0 { continue } for _, doc := range memberDocs { functionDocs := r.getLinkedPCIeFunctions(doc) if looksLikeGPU(doc, functionDocs) { continue } supplementalDocs := r.getLinkedSupplementalDocs(doc, "EnvironmentMetrics", "Metrics") supplementalDocs = append(supplementalDocs, r.getChassisScopedPCIeSupplementalDocs(doc)...) for _, fn := range functionDocs { supplementalDocs = append(supplementalDocs, r.getLinkedSupplementalDocs(fn, "EnvironmentMetrics", "Metrics")...) } dev := parsePCIeDeviceWithSupplementalDocs(doc, functionDocs, supplementalDocs) if isUnidentifiablePCIeDevice(dev) { continue } out = append(out, dev) } } for _, systemPath := range systemPaths { functionDocs, err := r.getCollectionMembers(joinPath(systemPath, "/PCIeFunctions")) if err != nil || len(functionDocs) == 0 { continue } for idx, fn := range functionDocs { supplementalDocs := r.getLinkedSupplementalDocs(fn, "EnvironmentMetrics", "Metrics") dev := parsePCIeFunctionWithSupplementalDocs(fn, supplementalDocs, idx+1) out = append(out, dev) } } return dedupePCIeDevices(out) } func (r redfishSnapshotReader) getChassisScopedPCIeSupplementalDocs(doc map[string]interface{}) []map[string]interface{} { if !looksLikeNVSwitchPCIeDoc(doc) { return nil } docPath := normalizeRedfishPath(asString(doc["@odata.id"])) chassisPath := chassisPathForPCIeDoc(docPath) if chassisPath == "" { return nil } out := make([]map[string]interface{}, 0, 4) for _, path := range []string{ joinPath(chassisPath, "/EnvironmentMetrics"), joinPath(chassisPath, "/ThermalSubsystem/ThermalMetrics"), } { supplementalDoc, err := r.getJSON(path) if err != nil || len(supplementalDoc) == 0 { continue } out = append(out, supplementalDoc) } return out } // collectBMCMAC returns the MAC address of the first active BMC management // interface found in Managers/*/EthernetInterfaces. Returns empty string if // no MAC is available. func (r redfishSnapshotReader) collectBMCMAC(managerPaths []string) string { for _, managerPath := range managerPaths { members, err := r.getCollectionMembers(joinPath(managerPath, "/EthernetInterfaces")) if err != nil || len(members) == 0 { continue } for _, doc := range members { mac := strings.TrimSpace(firstNonEmpty( asString(doc["PermanentMACAddress"]), asString(doc["MACAddress"]), )) if mac == "" || strings.EqualFold(mac, "00:00:00:00:00:00") { continue } return strings.ToUpper(mac) } } return "" } // enrichNICMACsFromNetworkDeviceFunctions reads the NetworkDeviceFunctions // collection linked from a NetworkAdapter document and populates the NIC's // MACAddresses from each function's Ethernet.PermanentMACAddress / MACAddress. // Called when PCIe-path enrichment does not produce any MACs. func (r redfishSnapshotReader) enrichNICMACsFromNetworkDeviceFunctions(nic *models.NetworkAdapter, adapterDoc map[string]interface{}) { ndfCol, ok := adapterDoc["NetworkDeviceFunctions"].(map[string]interface{}) if !ok { return } colPath := asString(ndfCol["@odata.id"]) if colPath == "" { return } funcDocs, err := r.getCollectionMembers(colPath) if err != nil || len(funcDocs) == 0 { return } for _, fn := range funcDocs { eth, _ := fn["Ethernet"].(map[string]interface{}) if eth == nil { continue } mac := strings.TrimSpace(firstNonEmpty( asString(eth["PermanentMACAddress"]), asString(eth["MACAddress"]), )) if mac == "" { continue } nic.MACAddresses = dedupeStrings(append(nic.MACAddresses, strings.ToUpper(mac))) } if len(funcDocs) > 0 && nic.PortCount == 0 { nic.PortCount = sanitizeNetworkPortCount(len(funcDocs)) } }