| #include "tlbmc/hal/fru_scanner_cable.h" |
| |
| #include <net/if.h> |
| |
| #include <cstdint> |
| #include <filesystem> // NOLINT |
| #include <fstream> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "bus_topology.pb.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/numbers.h" |
| #include "tlbmc/hal/fru_scanner.h" |
| |
| namespace milotic_tlbmc { |
| |
| namespace { |
| constexpr char kNetSysfsPath[] = "sys/class/net/"; |
| |
| // The NCSI cable is not always named "ncsi" If we want to expand the support |
| // for more platforms, we need to update the logic here. |
| constexpr char kNcsiPortName[] = "ncsi"; |
| |
| // Helper function to read a file and return its contents as a string |
| bool ReadOneLineFile(const std::filesystem::path& file_path, |
| std::string& content) { |
| std::ifstream ifs(file_path); |
| if (ifs.is_open() && std::getline(ifs, content)) { |
| ifs.close(); |
| return true; |
| } |
| return false; |
| } |
| |
| // Helper function for convert a hex string to uint64 type |
| // if string is invalid, return default val instead |
| uint64_t HexStringToUint64(const std::string& str, uint64_t def_val = 0) { |
| uint64_t val; |
| if (!absl::SimpleHexAtoi(str, &val)) { |
| return def_val; |
| } |
| return val; |
| } |
| |
| // Helper function for convert a decimal string to uint64 type |
| // if string is invalid, return default val instead |
| uint64_t DecStringToUint64(const std::string& str, uint64_t def_val = 0) { |
| uint64_t val; |
| if (!absl::SimpleAtoi(str, &val)) { |
| return def_val; |
| } |
| return val; |
| } |
| |
| // Checks if the NCSI cable is present by checking the carrier, flags, and |
| // rx_packets files in /sys/class/net/<port_name>/. For example, if port_name is |
| // "ncsi", it checks: |
| // /sys/class/net/ncsi/carrier - should contain "1" if carrier is present. |
| // /sys/class/net/ncsi/flags - should have IFF_UP bit set if interface is up. |
| // /sys/class/net/ncsi/statistics/rx_packets - should be > 0. |
| // Example sysfs structure for ncsi: |
| // /sys/class/net/ncsi/ |
| // ├── carrier |
| // ├── flags |
| // └── statistics/ |
| // └── rx_packets |
| bool IsNcsiCablePresent(const std::string& root_dir, |
| const std::string& port_name) { |
| std::filesystem::path carrier_path = |
| std::filesystem::path(root_dir) / kNetSysfsPath / port_name / "carrier"; |
| std::filesystem::path ifflag_path = |
| std::filesystem::path(root_dir) / kNetSysfsPath / port_name / "flags"; |
| std::filesystem::path rx_packets_path = std::filesystem::path(root_dir) / |
| kNetSysfsPath / port_name / |
| "statistics" / "rx_packets"; |
| |
| std::string flags_str; |
| std::string carrier_str; |
| std::string rx_packets_str; |
| if (!ReadOneLineFile(ifflag_path, flags_str) || |
| !ReadOneLineFile(carrier_path, carrier_str) || |
| !ReadOneLineFile(rx_packets_path, rx_packets_str)) { |
| return false; |
| } |
| return (HexStringToUint64(flags_str, 0) & IFF_UP) != 0 && |
| (DecStringToUint64(carrier_str, 0) == 1) && |
| (DecStringToUint64(rx_packets_str, 0) > 0); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<FruScannerCable> FruScannerCable::Create( |
| const FruScannerCable::Options& option) { |
| std::vector<uhmm::BmcMonitoredComponentsResponse::MonitoredComponent> |
| cable_components; |
| for (const auto& component : option.components) { |
| // Only NCSI components are supported by FruScannerCable as of now. |
| if (component.probe_path() != uhmm::BmcMonitoredComponentsResponse:: |
| MonitoredComponent::PROBE_PATH_NCSI) { |
| LOG(WARNING) << "FruScannerCable::Create: Skipping non-NCSI component: " |
| << component.barepath(); |
| continue; |
| } |
| |
| cable_components.push_back(component); |
| } |
| return absl::WrapUnique( |
| new FruScannerCable(std::move(cable_components), option.root_dir)); |
| } |
| |
| absl::StatusOr<std::vector<std::unique_ptr<CableFruInfo>>> |
| FruScannerCable::ScanAllCableFrus() const { |
| std::vector<std::unique_ptr<CableFruInfo>> cable_frus; |
| for (const auto& component : cable_components_) { |
| auto cable_fru = std::make_unique<CableFruInfo>(); |
| cable_fru->expected_barepath = component.barepath(); |
| cable_fru->present = IsNcsiCablePresent(root_dir_, kNcsiPortName); |
| cable_frus.push_back(std::move(cable_fru)); |
| } |
| return cable_frus; |
| } |
| |
| } // namespace milotic_tlbmc |