fwstatus: Enhance state of firmware inventory

Currently there is no mechanism for a user to know the status of their
firmware update over redfish.

The primary focus of this commit is to provide status of a firmware
update. There is still some ongoing discussion on using the Health field
to better explain error conditions. A forum post with DMTF has been
opened for clarification in this area but this commit does get things
going in the right direction. It provides a user with the status of
their update and gives them a simple Enabled/Disabled to know if it
worked.

Tested:

- Firmware update in progress
curl -k -H "X-Auth-Token: $TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory/92e57fc7
{
  "@odata.context": "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory",
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/92e57fc7",
  "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
  "Description": "BMC update",
  "Id": "92e57fc7",
  "Members@odata.count": 1,
  "Name": "Software Inventory",
  "RelatedItem": [
    {
      "@odata.id": "/redfish/v1/Managers/bmc"
    }
  ],
  "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Updating"
  },
  "Updateable": false,
  "Version": "2.7.0-dev-926-g79ca37b3d"
}

- Firmware update complete
curl -k -H "X-Auth-Token: $TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory/92e57fc7
{
  "@odata.context": "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory",
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/92e57fc7",
  "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
  "Description": "BMC update",
  "Id": "92e57fc7",
  "Members@odata.count": 1,
  "Name": "Software Inventory",
  "RelatedItem": [
    {
      "@odata.id": "/redfish/v1/Managers/bmc"
    }
  ],
  "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Enabled"
  },
  "Updateable": false,
  "Version": "2.7.0-dev-926-g79ca37b3d"
}

- Firmware update failed
(xyz.openbmc_project.Software.Activation.Activations.Failed)
curl -k -H "X-Auth-Token: $TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory/3d02a163
{
  "@odata.context": "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory",
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/3d02a163",
  "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
  "Description": "BMC update",
  "Id": "3d02a163",
  "Members@odata.count": 1,
  "Name": "Software Inventory",
  "RelatedItem": [
    {
      "@odata.id": "/redfish/v1/Managers/bmc"
    }
  ],
  "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Disabled"
  },
  "Updateable": false,
  "Version": "2.7.0-dev-940-geb79ec582"
}

- Firmware update status of Host
curl -k -H "X-Auth-Token: $TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory/9a8028ec
{
  "@odata.context": "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory",
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/9a8028ec",
  "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
  "Description": "Host update",
  "Id": "9a8028ec",
  "Name": "Software Inventory",
  "Status": {
    "Health": "OK",
    "HealthRollup": "OK",
    "State": "Enabled"
  },
  "Updateable": false,
  "Version": "IBM-witherspoon-OP9-v2.0.14-2.6"
}

Redfish validator had no additional errors

Change-Id: I26273227448cab1a20a20a7edd9d85d223727bb8
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/redfish-core/include/utils/fw_utils.hpp b/redfish-core/include/utils/fw_utils.hpp
index 18773ae..2341e8a 100644
--- a/redfish-core/include/utils/fw_utils.hpp
+++ b/redfish-core/include/utils/fw_utils.hpp
@@ -186,5 +186,89 @@
 
     return;
 }
+
+/**
+ * @brief Translate input fwState to Redfish state
+ *
+ * This function will return the corresponding Redfish state
+ *
+ * @param[i]   fwState  The OpenBMC firmware state
+ *
+ * @return The corresponding Redfish state
+ */
+std::string getRedfishFWState(const std::string &fwState)
+{
+    if (fwState == "xyz.openbmc_project.Software.Activation.Activations.Active")
+    {
+        return "Enabled";
+    }
+    else if (fwState ==
+             "xyz.openbmc_project.Software.Activation.Activations.Activating")
+    {
+        return "Updating";
+    }
+    else
+    {
+        BMCWEB_LOG_DEBUG << "Default fw state " << fwState << " to Disabled";
+        return "Disabled";
+    }
+}
+
+/**
+ * @brief Put status of input swId into json response
+ *
+ * This function will put the appropriate Redfish state of the input
+ * firmware id to ["Status"]["State"] within the json response
+ *
+ * @param[i,o] aResp    Async response object
+ * @param[i]   swId     The software ID to get status for
+ * @param[i]   dbusSvc  The dbus service implementing the software object
+ *
+ * @return void
+ */
+void getFwStatus(std::shared_ptr<AsyncResp> asyncResp,
+                 const std::shared_ptr<std::string> swId,
+                 const std::string &dbusSvc)
+{
+    BMCWEB_LOG_DEBUG << "getFwStatus: swId " << *swId << " svc " << dbusSvc;
+
+    crow::connections::systemBus->async_method_call(
+        [asyncResp,
+         swId](const boost::system::error_code error_code,
+               const boost::container::flat_map<std::string, VariantType>
+                   &propertiesList) {
+            if (error_code)
+            {
+                messages::internalError(asyncResp->res);
+                BMCWEB_LOG_ERROR
+                    << "getFwStatus: Error trying to get Activation for "
+                    << *swId;
+                return;
+            }
+            boost::container::flat_map<std::string, VariantType>::const_iterator
+                it = propertiesList.find("Activation");
+            if (it == propertiesList.end())
+            {
+                BMCWEB_LOG_DEBUG << "Can't find property \"Activation\"!";
+                messages::propertyMissing(asyncResp->res, "Activation");
+                return;
+            }
+            const std::string *swInvActivation =
+                std::get_if<std::string>(&it->second);
+            if (swInvActivation == nullptr)
+            {
+                BMCWEB_LOG_DEBUG << "wrong types for property\"Activation\"!";
+                messages::propertyValueTypeError(asyncResp->res, "",
+                                                 "Activation");
+                return;
+            }
+            BMCWEB_LOG_DEBUG << "getFwStatus: Activation " << *swInvActivation;
+            asyncResp->res.jsonValue["Status"]["State"] =
+                getRedfishFWState(*swInvActivation);
+        },
+        dbusSvc, "/xyz/openbmc_project/software/" + *swId,
+        "org.freedesktop.DBus.Properties", "GetAll",
+        "xyz.openbmc_project.Software.Activation");
+}
 } // namespace fw_util
 } // namespace redfish
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 7e08ec7..413e39c 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -499,58 +499,14 @@
                     }
                     std::string swId = obj.first.substr(idPos + 1);
 
-                    for (auto &conn : connections)
-                    {
-                        const std::string &connectionName = conn.first;
-                        BMCWEB_LOG_DEBUG << "connectionName = "
-                                         << connectionName;
-                        BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
-
-                        crow::connections::systemBus->async_method_call(
-                            [asyncResp,
-                             swId](const boost::system::error_code error_code,
-                                   const VariantType &activation) {
-                                BMCWEB_LOG_DEBUG
-                                    << "safe returned in lambda function";
-                                if (error_code)
-                                {
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-
-                                const std::string *swActivationStatus =
-                                    std::get_if<std::string>(&activation);
-                                if (swActivationStatus == nullptr)
-                                {
-                                    messages::internalError(asyncResp->res);
-                                    return;
-                                }
-                                if (swActivationStatus != nullptr &&
-                                    *swActivationStatus !=
-                                        "xyz.openbmc_project.Software."
-                                        "Activation."
-                                        "Activations.Active")
-                                {
-                                    // The activation status of this software is
-                                    // not currently active, so does not need to
-                                    // be listed in the response
-                                    return;
-                                }
-                                nlohmann::json &members =
-                                    asyncResp->res.jsonValue["Members"];
-                                members.push_back(
-                                    {{"@odata.id", "/redfish/v1/UpdateService/"
-                                                   "FirmwareInventory/" +
-                                                       swId}});
-                                asyncResp->res
-                                    .jsonValue["Members@odata.count"] =
-                                    members.size();
-                            },
-                            connectionName, obj.first,
-                            "org.freedesktop.DBus.Properties", "Get",
-                            "xyz.openbmc_project.Software.Activation",
-                            "Activation");
-                    }
+                    nlohmann::json &members =
+                        asyncResp->res.jsonValue["Members"];
+                    members.push_back(
+                        {{"@odata.id", "/redfish/v1/UpdateService/"
+                                       "FirmwareInventory/" +
+                                           swId}});
+                    asyncResp->res.jsonValue["Members@odata.count"] =
+                        members.size();
                 }
             },
             "xyz.openbmc_project.ObjectMapper",
@@ -617,7 +573,6 @@
         res.jsonValue["Updateable"] = false;
         res.jsonValue["Status"]["Health"] = "OK";
         res.jsonValue["Status"]["HealthRollup"] = "OK";
-        res.jsonValue["Status"]["State"] = "Enabled";
 
         if (params.size() != 1)
         {
@@ -662,6 +617,8 @@
                         continue;
                     }
 
+                    fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
+
                     crow::connections::systemBus->async_method_call(
                         [asyncResp,
                          swId](const boost::system::error_code error_code,