Add "Links""SoftwareImages"

SoftwareImages was added in Manager in 1_6_0 and Bios in 1_1_0.

Use the existing getActiveFwVersion function but rename to
populateFirmwareInformation. Changed the mapper call from a GetObject to a
GetSubTree. Added some debug to the function.

Tested: Validator passes.

curl -k https://$bmc/redfish/v1/Managers/bmc
 ....
  "Links": {
    "ActiveSoftwareImage": {
      "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/e4e1c69d"
    },
    "ManagerForChassis": [
 ....
      "@odata.id": "/redfish/v1/Chassis/chassis"
    },
    "SoftwareImages": [
      {
        "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/730944ed"
      },
      {
        "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/e4e1c69d"
      }
    ],
    "SoftwareImages@odata.count": 2
  },
...

Change-Id: I20798852a2f62575854820bff36175dda38c7dbc
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/redfish-core/include/utils/fw_utils.hpp b/redfish-core/include/utils/fw_utils.hpp
index 65f19ca..8e04070 100644
--- a/redfish-core/include/utils/fw_utils.hpp
+++ b/redfish-core/include/utils/fw_utils.hpp
@@ -1,8 +1,10 @@
 #pragma once
 #include <async_resp.hpp>
 
+#include <algorithm>
 #include <string>
 #include <variant>
+#include <vector>
 
 namespace redfish
 {
@@ -17,28 +19,28 @@
     "xyz.openbmc_project.Software.Version.VersionPurpose.BMC";
 
 /**
- * @brief Put fw version of input type into async response json structure
+ * @brief Populate the running firmware version and image links
  *
  * @param[i,o] aResp             Async response object
  * @param[i]   fwVersionPurpose  Indicates what target to look for
  * @param[i]   activeVersionPropName  Index in aResp->res.jsonValue to write
  * the running firmware version to
- * @param[i]   populateLinkToActiveImage  Populate aResp->res "Links"
- * "ActiveSoftwareImage" with a link to the running firmware image
+ * @param[i]   populateLinkToImages  Populate aResp->res "Links"
+ * "ActiveSoftwareImage" with a link to the running firmware image and
+ * "SoftwareImages" with a link to the all its firmware images
  *
  * @return void
  */
-inline void getActiveFwVersion(std::shared_ptr<AsyncResp> aResp,
-                               const std::string& fwVersionPurpose,
-                               const std::string& activeVersionPropName,
-                               const bool populateLinkToActiveImage)
+inline void populateFirmwareInformation(
+    std::shared_ptr<AsyncResp> aResp, const std::string& fwVersionPurpose,
+    const std::string& activeVersionPropName, const bool populateLinkToImages)
 {
-    // Get active FW images
+    // Used later to determine running (known on Redfish as active) FW images
     crow::connections::systemBus->async_method_call(
-        [aResp, fwVersionPurpose, activeVersionPropName,
-         populateLinkToActiveImage](
+        [aResp, fwVersionPurpose, activeVersionPropName, populateLinkToImages](
             const boost::system::error_code ec,
             const std::variant<std::vector<std::string>>& resp) {
+            BMCWEB_LOG_DEBUG << "populateFirmwareInformation enter";
             if (ec)
             {
                 BMCWEB_LOG_ERROR << "error_code = " << ec;
@@ -46,76 +48,97 @@
                 messages::internalError(aResp->res);
                 return;
             }
+
             const std::vector<std::string>* functionalFw =
                 std::get_if<std::vector<std::string>>(&resp);
             if ((functionalFw == nullptr) || (functionalFw->size() == 0))
             {
+                // Could keep going and try to populate SoftwareImages but
+                // something is seriously wrong, so just fail
                 BMCWEB_LOG_ERROR << "Zero functional software in system";
                 messages::internalError(aResp->res);
                 return;
             }
+
+            std::vector<std::string> functionalFwIds;
             // example functionalFw:
             // v as 2 "/xyz/openbmc_project/software/ace821ef"
             //        "/xyz/openbmc_project/software/230fb078"
             for (auto& fw : *functionalFw)
             {
-                // if can't parse fw id then return
+
                 std::string::size_type idPos = fw.rfind("/");
                 if (idPos == std::string::npos)
                 {
-                    messages::internalError(aResp->res);
-                    BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
-                    return;
+                    BMCWEB_LOG_DEBUG << "Can't parse firmware ID!";
+                    continue;
                 }
                 idPos++;
                 if (idPos >= fw.size())
                 {
-                    messages::internalError(aResp->res);
                     BMCWEB_LOG_DEBUG << "Invalid firmware ID";
-                    return;
+                    continue;
                 }
-                std::string swId = fw.substr(idPos);
+                functionalFwIds.push_back(fw.substr(idPos));
+            }
 
-                // Now find service that hosts it
-                crow::connections::systemBus->async_method_call(
-                    [aResp, fw, swId, fwVersionPurpose, activeVersionPropName,
-                     populateLinkToActiveImage](
-                        const boost::system::error_code ec2,
-                        const std::vector<std::pair<
-                            std::string, std::vector<std::string>>>& objInfo) {
-                        if (ec2)
+            crow::connections::systemBus->async_method_call(
+                [aResp, fwVersionPurpose, activeVersionPropName,
+                 populateLinkToImages, functionalFwIds](
+                    const boost::system::error_code ec2,
+                    const std::vector<
+                        std::pair<std::string,
+                                  std::vector<std::pair<
+                                      std::string, std::vector<std::string>>>>>&
+                        subtree) {
+                    if (ec2)
+                    {
+                        BMCWEB_LOG_ERROR << "error_code = " << ec2;
+                        BMCWEB_LOG_ERROR << "error msg = " << ec2.message();
+                        messages::internalError(aResp->res);
+                        return;
+                    }
+
+                    BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " images";
+
+                    for (const std::pair<
+                             std::string,
+                             std::vector<std::pair<
+                                 std::string, std::vector<std::string>>>>& obj :
+                         subtree)
+                    {
+                        // if can't parse fw id then return
+                        std::string::size_type idPos = obj.first.rfind("/");
+                        if (idPos == std::string::npos)
                         {
-                            BMCWEB_LOG_DEBUG << "error_code = " << ec2;
-                            BMCWEB_LOG_DEBUG << "error msg = " << ec2.message();
                             messages::internalError(aResp->res);
+                            BMCWEB_LOG_ERROR << "Can't parse firmware ID!!";
                             return;
                         }
-                        // Example objInfo
-                        // a{sas} 1 "org.open_power.Software.Host.Updater" 10
-                        // "org.freedesktop.DBus.Introspectable"
-                        // "org.freedesktop.DBus.Peer"
-                        // "org.freedesktop.DBus.Properties"
-                        // "org.openbmc.Associations"
-                        // "xyz.openbmc_project.Common.FilePath"
-                        // "xyz.openbmc_project.Object.Delete"
-                        // "xyz.openbmc_project.Software.Activation"
-                        // "xyz.openbmc_project.Software.ExtendedVersion"
-                        // "xyz.openbmc_project.Software.RedundancyPriority"
-                        // "xyz.openbmc_project.Software.Version"
-
-                        // Ensure we only got one service back
-                        if (objInfo.size() != 1)
+                        idPos++;
+                        if (idPos >= obj.first.size())
                         {
-                            BMCWEB_LOG_ERROR << "Invalid Object Size "
-                                             << objInfo.size();
                             messages::internalError(aResp->res);
+                            BMCWEB_LOG_ERROR << "Invalid firmware ID";
                             return;
                         }
+                        std::string swId = obj.first.substr(idPos);
+
+                        bool runningImage = false;
+                        // Look at Ids from
+                        // /xyz/openbmc_project/software/functional
+                        // to determine if this is a running image
+                        if (std::find(functionalFwIds.begin(),
+                                      functionalFwIds.end(),
+                                      swId) != functionalFwIds.end())
+                        {
+                            runningImage = true;
+                        }
 
                         // Now grab its version info
                         crow::connections::systemBus->async_method_call(
-                            [aResp, swId, fwVersionPurpose,
-                             activeVersionPropName, populateLinkToActiveImage](
+                            [aResp, swId, runningImage, fwVersionPurpose,
+                             activeVersionPropName, populateLinkToImages](
                                 const boost::system::error_code ec3,
                                 const boost::container::flat_map<
                                     std::string,
@@ -142,7 +165,7 @@
                                     propertiesList.find("Purpose");
                                 if (it == propertiesList.end())
                                 {
-                                    BMCWEB_LOG_DEBUG
+                                    BMCWEB_LOG_ERROR
                                         << "Can't find property \"Purpose\"!";
                                     messages::internalError(aResp->res);
                                     return;
@@ -151,37 +174,61 @@
                                     std::get_if<std::string>(&it->second);
                                 if (swInvPurpose == nullptr)
                                 {
-                                    BMCWEB_LOG_DEBUG << "wrong types for "
+                                    BMCWEB_LOG_ERROR << "wrong types for "
                                                         "property \"Purpose\"!";
                                     messages::internalError(aResp->res);
                                     return;
                                 }
 
+                                BMCWEB_LOG_DEBUG << "Image ID: " << swId;
+                                BMCWEB_LOG_DEBUG << "Image purpose: "
+                                                 << *swInvPurpose;
+                                BMCWEB_LOG_DEBUG << "Running image: "
+                                                 << runningImage;
+
                                 if (*swInvPurpose != fwVersionPurpose)
                                 {
                                     // Not purpose we're looking for
                                     return;
                                 }
-                                if (populateLinkToActiveImage)
+
+                                if (populateLinkToImages)
                                 {
+                                    nlohmann::json& softwareImageMembers =
+                                        aResp->res.jsonValue["Links"]
+                                                            ["SoftwareImages"];
                                     // Firmware images are at
                                     // /redfish/v1/UpdateService/FirmwareInventory/<Id>
                                     // e.g. .../FirmwareInventory/82d3ec86
-                                    // Create the link to the running one
-                                    aResp->res
-                                        .jsonValue["Links"]
-                                                  ["ActiveSoftwareImage"] = {
-                                        {"@odata.id",
-                                         "/redfish/v1/UpdateService/"
-                                         "FirmwareInventory/" +
-                                             swId}};
+                                    softwareImageMembers.push_back(
+                                        {{"@odata.id",
+                                          "/redfish/v1/UpdateService/"
+                                          "FirmwareInventory/" +
+                                              swId}});
+                                    aResp->res.jsonValue
+                                        ["Links"]
+                                        ["SoftwareImages@odata.count"] =
+                                        softwareImageMembers.size();
+
+                                    if (runningImage)
+                                    {
+                                        // Create the link to the running image
+                                        aResp->res
+                                            .jsonValue["Links"]
+                                                      ["ActiveSoftwareImage"] =
+                                            {{"@odata.id",
+                                              "/redfish/v1/UpdateService/"
+                                              "FirmwareInventory/" +
+                                                  swId}};
+                                    }
                                 }
-                                if (!activeVersionPropName.empty())
+                                if (!activeVersionPropName.empty() &&
+                                    runningImage)
                                 {
                                     it = propertiesList.find("Version");
                                     if (it == propertiesList.end())
                                     {
-                                        BMCWEB_LOG_DEBUG
+                                        BMCWEB_LOG_ERROR
                                             << "Can't find property "
                                                "\"Version\"!";
                                         messages::internalError(aResp->res);
@@ -191,7 +238,7 @@
                                         std::get_if<std::string>(&it->second);
                                     if (version == nullptr)
                                     {
-                                        BMCWEB_LOG_DEBUG
+                                        BMCWEB_LOG_ERROR
                                             << "Error getting fw version";
                                         messages::internalError(aResp->res);
                                         return;
@@ -202,16 +249,17 @@
                                         *version;
                                 }
                             },
-                            objInfo[0].first, fw,
+                            obj.second[0].first, obj.first,
                             "org.freedesktop.DBus.Properties", "GetAll",
                             "xyz.openbmc_project.Software.Version");
-                    },
-                    "xyz.openbmc_project.ObjectMapper",
-                    "/xyz/openbmc_project/object_mapper",
-                    "xyz.openbmc_project.ObjectMapper", "GetObject", fw,
-                    std::array<const char*, 1>{
-                        "xyz.openbmc_project.Software.Activation"});
-            }
+                    }
+                },
+                "xyz.openbmc_project.ObjectMapper",
+                "/xyz/openbmc_project/object_mapper",
+                "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+                "/xyz/openbmc_project/software", static_cast<int32_t>(0),
+                std::array<const char*, 1>{
+                    "xyz.openbmc_project.Software.Version"});
         },
         "xyz.openbmc_project.ObjectMapper",
         "/xyz/openbmc_project/software/functional",
diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
index 8acf29b..2c31077 100644
--- a/redfish-core/lib/bios.hpp
+++ b/redfish-core/lib/bios.hpp
@@ -32,8 +32,9 @@
             {"target",
              "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"}};
 
-        // Get the ActiveSoftwareImage
-        fw_util::getActiveFwVersion(asyncResp, fw_util::biosPurpose, "", true);
+        // Get the ActiveSoftwareImage and SoftwareImages
+        fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose,
+                                             "", true);
     }
 };
 /**
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 1d13cd3..f674766 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -1793,8 +1793,8 @@
         health->isManagersHealth = true;
         health->populate();
 
-        fw_util::getActiveFwVersion(asyncResp, fw_util::bmcPurpose,
-                                    "FirmwareVersion", true);
+        fw_util::populateFirmwareInformation(asyncResp, fw_util::bmcPurpose,
+                                             "FirmwareVersion", true);
 
         getLastResetTime(asyncResp);
 
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index fb1288e..5d96649 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -533,7 +533,7 @@
                                     }
 
                                     // Grab the bios version
-                                    fw_util::getActiveFwVersion(
+                                    fw_util::populateFirmwareInformation(
                                         aResp, fw_util::biosPurpose,
                                         "BiosVersion", false);
                                 },