Add support for parsing DIMM configurations from EM config.

Introduce the `RESOURCE_TYPE_DIMM` and implement `ParseDimmConfig` to handle DIMM entries in the entity config JSON. Similar to Processors, DIMMs are now parsed from EM config and generate their own FRU and TopologyConfig node to be able to be referred to by the RelatedItem of sensors and provide the correct devpath for this object.

#tlbmc

PiperOrigin-RevId: 819963582
Change-Id: I44115446ef54c1c03e8f4fe4173a3f6bee69d2e6
diff --git a/tlbmc/configs/entity_config_json_impl.cc b/tlbmc/configs/entity_config_json_impl.cc
index 9f336a6..2d41528 100644
--- a/tlbmc/configs/entity_config_json_impl.cc
+++ b/tlbmc/configs/entity_config_json_impl.cc
@@ -106,6 +106,7 @@
 constexpr absl::string_view kChassisTypeKeyword = "ChassisType";
 constexpr absl::string_view kStorageKeyword = "Storage";
 constexpr absl::string_view kProcessorKeyword = "Processor";
+constexpr absl::string_view kDimmKeyword = "DIMM";
 constexpr absl::string_view kFanKeyword = "Fan";
 constexpr absl::string_view kRelatedItemKeyword = "RelatedItem";
 constexpr absl::string_view kResourceTypeKeyword = "ResourceType";
@@ -1000,6 +1001,10 @@
   return type == "NicTelemetry";
 }
 
+bool EntityConfigJsonImpl::IsDimm(std::string_view type) {
+  return type == kDimmKeyword;
+}
+
 absl::Status EntityConfigJsonImpl::ParseAndPopulateConfig(
     EntityConfigJsonImplImmutableData& data, const nlohmann::json& element,
     absl::string_view config_name_with_index, bool is_subfru,
@@ -1106,7 +1111,7 @@
   }
 
   // We support these types but they need to be parsed later.
-  if (IsFan(*type)) {
+  if (IsFan(*type) || IsDimm(*type)) {
     tlbmc_supported = true;
   }
 
@@ -1421,6 +1426,17 @@
               .max_updates_to_mutable_data = ad_hoc_fru_count};
         }
 
+        absl::Status parse_dimm_status =
+            ParseDimmConfig(mutable_data, element, topology_config_node,
+                            config_name_with_index);
+        if (!parse_dimm_status.ok()) {
+          mutable_data.parsed_status = parse_dimm_status;
+          return EntityConfigJsonImplData{
+              .immutable_data = std::move(immutable_data),
+              .mutable_data = std::move(mutable_data),
+              .max_updates_to_mutable_data = ad_hoc_fru_count};
+        }
+
         absl::Status parse_fan_status =
             ParseFanConfig(mutable_data, element, topology_config_node,
                            config_name_with_index);
@@ -2579,6 +2595,74 @@
   return absl::OkStatus();
 }
 
+absl::Status EntityConfigJsonImpl::ParseDimmConfig(
+    EntityConfigJsonImplMutableData& mutable_data, const nlohmann::json& config,
+    TopologyConfigNode& topology_config_node,
+    std::string_view top_level_config_name) {
+  const bool* tlbmc_owned = GetValueAsBool(config, "TlbmcOwned");
+  // If not tlbmc owned then ignore
+  if (tlbmc_owned == nullptr || !*tlbmc_owned) {
+    return absl::OkStatus();
+  }
+  const std::string* type_str = GetValueAsString(config, "Type");
+  if (type_str == nullptr || *type_str != kDimmKeyword) {
+    return absl::OkStatus();
+  }
+
+  const std::string* dimm_name = GetValueAsString(config, "Name");
+  if (dimm_name == nullptr) {
+    return absl::InvalidArgumentError(
+        "Invalid config: Name field is not found for dimm config");
+  }
+
+  const std::string* service_label = GetValueAsString(config, "ServiceLabel");
+  if (service_label == nullptr) {
+    return absl::InvalidArgumentError(
+        "Invalid config: ServiceLabel field is not found for dimm config");
+  }
+
+  // Each dimm should have a unique node name
+  std::string dimm_node_name =
+      absl::StrFormat("%s_%s", top_level_config_name, *dimm_name);
+
+  // Add dimm to fru table.
+  Fru dimm_fru;
+  dimm_fru.mutable_attributes()->set_resource_type(RESOURCE_TYPE_DIMM);
+  dimm_fru.mutable_attributes()->set_key(dimm_node_name);
+  dimm_fru.mutable_attributes()->set_status(Status::STATUS_READY);
+
+  // Dimm should be its own Topology Node as it has its own machine level
+  // devpath
+  TopologyConfigNode dimm_node;
+  dimm_node.set_name(*dimm_name);
+  dimm_node.mutable_fru_info()->set_fru_key(dimm_node_name);
+  dimm_node.set_config_key(dimm_node_name);
+
+  // Add topology port configs for upstream and downstream
+  PortConfig chassis_to_dimm_port_config;
+  chassis_to_dimm_port_config.set_port_type(PORT_TYPE_DOWNSTREAM);
+  chassis_to_dimm_port_config.set_port_name(dimm_node_name);
+  chassis_to_dimm_port_config.set_port_label(*service_label);
+  topology_config_node.mutable_port_configs()->insert(
+      {dimm_node_name, std::move(chassis_to_dimm_port_config)});
+
+  PortConfig dimm_to_chassis_port_config;
+  dimm_to_chassis_port_config.set_port_type(PORT_TYPE_UPSTREAM);
+  dimm_to_chassis_port_config.set_port_name(dimm_node_name);
+  *dimm_node.add_upstream_port_configs() =
+      std::move(dimm_to_chassis_port_config);
+
+  // The config name and the fru key are identical.
+  mutable_data.topology_config.mutable_topology_config_nodes()->insert(
+      {dimm_node_name, std::move(dimm_node)});
+  mutable_data.topology_config.mutable_fru_configs()->insert(
+      {dimm_node_name, dimm_node_name});
+  mutable_data.fru_table.mutable_key_to_fru()->insert(
+      {dimm_node_name, std::move(dimm_fru)});
+
+  return absl::OkStatus();
+}
+
 absl::Status EntityConfigJsonImpl::LinkParentChildNodes(
     Fru& current_fru, TopologyConfigNode& current_node, Fru& downstream_fru,
     TopologyConfigNode& downstream_node) {
@@ -2603,6 +2687,10 @@
              RESOURCE_TYPE_PROCESSOR) {
     current_node.mutable_children_processors()->insert(
         {downstream_node.name(), downstream_node.config_key()});
+  } else if (downstream_fru.attributes().resource_type() ==
+             RESOURCE_TYPE_DIMM) {
+    current_node.mutable_children_dimms()->insert(
+        {downstream_node.name(), downstream_node.config_key()});
   }
 
   // Link current node <- downstream node.
diff --git a/tlbmc/configs/entity_config_json_impl.h b/tlbmc/configs/entity_config_json_impl.h
index a8d18b1..da04c97 100644
--- a/tlbmc/configs/entity_config_json_impl.h
+++ b/tlbmc/configs/entity_config_json_impl.h
@@ -269,6 +269,8 @@
 
   static bool IsNicTelemetry(std::string_view type);
 
+  static bool IsDimm(std::string_view type);
+
   // Parses the Hwmon common config from the JSON config.
   static absl::StatusOr<HalCommonConfig> ParseHalCommonConfig(
       const nlohmann::json& config);
@@ -353,6 +355,11 @@
       const nlohmann::json& config, TopologyConfigNode& topology_config_node,
       std::string_view top_level_config_name);
 
+  static absl::Status ParseDimmConfig(
+      EntityConfigJsonImplMutableData& mutable_data,
+      const nlohmann::json& config, TopologyConfigNode& topology_config_node,
+      std::string_view top_level_config_name);
+
   static absl::Status ParseFanConfig(
       EntityConfigJsonImplMutableData& mutable_data,
       const nlohmann::json& config, TopologyConfigNode& topology_config_node,
diff --git a/tlbmc/resource.proto b/tlbmc/resource.proto
index 3a5f67a..deace92 100644
--- a/tlbmc/resource.proto
+++ b/tlbmc/resource.proto
@@ -51,6 +51,7 @@
   RESOURCE_TYPE_COMPUTER_SYSTEM = 5;
   RESOURCE_TYPE_FAN = 6;
   RESOURCE_TYPE_ASSEMBLY = 7;
+  RESOURCE_TYPE_DIMM = 8;
 }
 
 // Type of the chassis, used when resource type is RESOURCE_TYPE_BOARD.
diff --git a/tlbmc/topology_config.proto b/tlbmc/topology_config.proto
index 9a06254..3f15be6 100644
--- a/tlbmc/topology_config.proto
+++ b/tlbmc/topology_config.proto
@@ -73,6 +73,7 @@
   // topology node key.
   map<string, string> children_processors = 12;
   map<string, string> children_fans = 20;
+  map<string, string> children_dimms = 21;
   // Only chassis and cable are possible parents.
   repeated string parent_chassis_ids = 17;
   repeated string parent_cable_ids = 18;