Refactor SubFru definition to be based on the ResourceType instead of "IsSubFru" field

It is incorrect to allow SubFrus to be defined as `RESOURCE_TYPE_BOARD`. A subfru should be defined as a logical or physical resource attached to a Fru but cannot be removed from the Fru and is not detectable on its own. This refactor will prevent SubFrus from being defined as `RESOURCE_TYPE_BOARD` and must be defined with `RESOURCE_TYPE_ASSEMBLY` instead.

Since these are subfru objects, they must be defined with `PART_LOCATION_TYPE_EMBEDDED`. This requirement is still enforced. Since these will now be populated under the Assembly collection which is not yet owned by tlBMC, configs with `RESOURCE_TYPE_ASSEMBLY` are no longer expected in the redfish response.

Follow up cl/758881979 will propagate sensors from children assemblies to the parent Fru as described in go/tlbmc-redfish-gf-tree-assimilation.

#tlbmc

PiperOrigin-RevId: 761720588
Change-Id: If006d6c88aac636a52ad23b1bcebbaee3e938710
diff --git a/tlbmc/configs/entity_config_json_impl.cc b/tlbmc/configs/entity_config_json_impl.cc
index c187523..dbdee5f 100644
--- a/tlbmc/configs/entity_config_json_impl.cc
+++ b/tlbmc/configs/entity_config_json_impl.cc
@@ -423,14 +423,6 @@
   return find_key->get<nlohmann::json>();
 }
 
-bool IsSubFru(const nlohmann::json& probe) {
-  const bool* is_sub_fru = GetValueAsBool(probe, "IsSubFru");
-  if (is_sub_fru == nullptr) {
-    return false;
-  }
-  return *is_sub_fru;
-}
-
 absl::StatusOr<std::string> ParseConfigName(const nlohmann::json& config) {
   const std::string* name = GetValueAsString(config, "Name");
   if (name == nullptr) {
@@ -939,8 +931,17 @@
   // config.
   for (const auto& [name, config_data] : *probed_config_map) {
     // Check if config belongs to a sub FRU.
-    // We have already checked that the config has a probe in `ProcessConfigs`.
-    bool is_subfru = IsSubFru(config_data.config.at(kProbeKeyword));
+    // RESOURCE_TYPE_ASSEMBLY indicates a config that is a sub FRU.
+    absl::StatusOr<ResourceType> resource_type =
+        ParseResourceType(config_data.config);
+
+    if (!resource_type.ok()) {
+      mutable_data.parsed_status = resource_type.status();
+      return EntityConfigJsonImplData{
+          .immutable_data = std::move(immutable_data),
+          .mutable_data = std::move(mutable_data),
+          .max_updates_to_mutable_data = ad_hoc_fru_count};
+    }
 
     // For each FRU, substitute config variables with FRU info and parse the
     // config.
@@ -954,17 +955,20 @@
                           &config_name_with_index);
       topology_config_node.set_name(config_name_with_index);
 
-      const std::string fru_key = config_data.fru_keys[i].ToString();
+      std::string fru_key = config_data.fru_keys[i].ToString();
 
-      absl::StatusOr<ResourceType> resource_type =
-          ParseResourceType(config_data.config);
-
-      if (!resource_type.ok()) {
-        mutable_data.parsed_status = resource_type.status();
-        return EntityConfigJsonImplData{
-            .immutable_data = std::move(immutable_data),
-            .mutable_data = std::move(mutable_data),
-            .max_updates_to_mutable_data = ad_hoc_fru_count};
+      // In the case of a sub FRU, we create an entry in the FruTable keyed by
+      // the config name to have separate Fru data from the parent Fru.
+      if (*resource_type == RESOURCE_TYPE_ASSEMBLY) {
+        fru_key = config_name_with_index;
+        Fru sub_fru_info;
+        Attributes* attributes = sub_fru_info.mutable_attributes();
+        attributes->set_key(fru_key);
+        attributes->set_status(Status::STATUS_READY);
+        attributes->mutable_refresh_policy()->set_refresh_mode(
+            RefreshMode::REFRESH_MODE_ON_DEMAND);
+        mutable_data.fru_table.mutable_key_to_fru()->insert(
+            {fru_key, sub_fru_info});
       }
 
       // Get the FRU object from the fru_key. This is used to update the Fru
@@ -981,16 +985,6 @@
 
       ParsePartLocationType(config_data.config, topology_config_node);
       ParseRootChassisLocationCode(config_data.config, topology_config_node);
-      if (is_subfru &&
-          topology_config_node.location_context().location_type() !=
-              PART_LOCATION_TYPE_EMBEDDED) {
-        mutable_data.parsed_status = absl::InternalError(absl::StrCat(
-            "Invalid config: Sub FRU ", topology_config_node.name(),
-            " has part location type ",
-            topology_config_node.location_context().location_type(),
-            ". All sub FRUs should have part location type "
-            "PART_LOCATION_TYPE_EMBEDDED."));
-      }
 
       absl::StatusOr<nlohmann::json> exposes =
           GetObject(config_data.config, "Exposes");
@@ -1145,15 +1139,17 @@
       }
 
       topology_config_node.mutable_fru_info()->set_fru_key(fru_key);
-      if (!is_subfru) {
-        mutable_data.topology_config.mutable_fru_configs()->insert(
-            {fru_key, config_name_with_index});
-      } else {
+
+      mutable_data.topology_config.mutable_fru_configs()->insert(
+          {fru_key, config_name_with_index});
+      if (*resource_type == RESOURCE_TYPE_ASSEMBLY) {
         topology_config_node.mutable_fru_info()->set_is_sub_fru(true);
-        fru_key_to_sub_fru_config[fru_key].insert(config_name_with_index);
+        fru_key_to_sub_fru_config[config_data.fru_keys[i].ToString()].insert(
+            config_name_with_index);
       }
 
-      if (fru_info.attributes().resource_type() == RESOURCE_TYPE_CABLE) {
+      if (fru_info.attributes().resource_type() == RESOURCE_TYPE_CABLE ||
+          fru_info.attributes().resource_type() == RESOURCE_TYPE_ASSEMBLY) {
         topology_config_node.set_not_owned_by_tlbmc(true);
       }
 
@@ -1899,6 +1895,9 @@
       downstream_node.set_parent_resource_id(current_node->name());
       if (downstream_fru.attributes().resource_type() == RESOURCE_TYPE_BOARD) {
         current_node->add_children_chassis_ids(downstream_node.name());
+      } else if (downstream_fru.attributes().resource_type() ==
+                 RESOURCE_TYPE_ASSEMBLY) {
+        current_node->add_children_assembly_ids(downstream_node.name());
       }
       node_queue.push(&downstream_node);
     }
diff --git a/tlbmc/redfish/data/stable_id.cc b/tlbmc/redfish/data/stable_id.cc
index 862c22a..36a6e61 100644
--- a/tlbmc/redfish/data/stable_id.cc
+++ b/tlbmc/redfish/data/stable_id.cc
@@ -61,9 +61,6 @@
 
   if (location_context.has_location_type()) {
     stable_id.set_location_type(location_context.location_type());
-    if (stable_id.location_type() == PART_LOCATION_TYPE_EMBEDDED) {
-      stable_id.set_service_label(topology_config_node.name());
-    }
   }
 
   return stable_id;
diff --git a/tlbmc/resource.proto b/tlbmc/resource.proto
index 2356eb0..bcce08d 100644
--- a/tlbmc/resource.proto
+++ b/tlbmc/resource.proto
@@ -43,6 +43,7 @@
   RESOURCE_TYPE_STORAGE = 4;
   RESOURCE_TYPE_COMPUTER_SYSTEM = 5;
   RESOURCE_TYPE_FAN = 6;
+  RESOURCE_TYPE_ASSEMBLY = 7;
 }
 
 // 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 f446ce9..46af327 100644
--- a/tlbmc/topology_config.proto
+++ b/tlbmc/topology_config.proto
@@ -75,6 +75,7 @@
   repeated string children_cable_ids = 11;
   repeated string children_processor_ids = 12;
   repeated string children_storage_ids = 13;
+  repeated string children_assembly_ids = 16;
   // Whether the config in this node is supported by tlBMC query. If true, this
   // node will not be returned in GetAllConfigNames() since this config is not
   // supported by tlBMC at the moment.