Add Redfish ThermalSubsystem schema in bmcweb

The ThermalSubsystem is a new resource in Redfish version 2020.4.
It is a root for fans and temperatures. Fans are a new schema.
Temperature sensors will be part of the new ThermalMetrics schema.

ThermalSubsystem can co-exist with the current Thermal resource.
You can also control compilation through flags.

ThermalSubsystem is an improvement on the existing Thermal schema
because
1. It includes the latest properties like LocationIndicatorActive
2. Fans and Temperatures were arrays in the old Thermal schema and
   this was cumbersome and could hit limits of JSON arrays
3. Large amount of static data mixed with sensor readings, which
   hurt performance
4. Inconsistent definitions of properties vs like Processor and
   Memory schemas

In a future commits Fans and ThermalMetrics will be added soon.

Reference:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0268_2020.4.pdf
https://redfish.dmtf.org/schemas/v1/ThermalSubsystem.v1_0_0.json

Test:
1. Validator passed.
2. doGet method:
~$ curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/ThermalSubsystem
{
  "@odata.id": "/redfish/v1/Chassis/chassis/ThermalSubsystem",
  "@odata.type": "#ThermalSubsystem.v1_0_0.ThermalSubsystem",
  "Id": "chassis",
  "Name": "Thermal Subsystem for Chassis",
  "Status": {
    "Health": "OK",
    "State": "Enabled"
  }
}
3. A bad chassis ID:
~$ curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassisSSBAD/ThermalSubsystem
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The requested resource of type Chassis named chassisSSBAD was not found.",
        "MessageArgs": [
          "Chassis",
          "chassisSSBAD"
        ],
        "MessageId": "Base.1.8.1.ResourceNotFound",
        "MessageSeverity": "Critical",
        "Resolution": "Provide a valid resource identifier and resubmit the request."
      }
    ],
    "code": "Base.1.8.1.ResourceNotFound",
    "message": "The requested resource of type Chassis named chassisSSBAD was not found."
  }
}

Signed-off-by: Xiaochao Ma <maxiaochao@inspur.com>
Change-Id: Ib19879f584304e5303f1a83d88bdd18c78a61633
Signed-off-by: Zhenwei Chen <zhenweichen0207@gmail.com>
diff --git a/redfish-core/lib/thermal_subsystem.hpp b/redfish-core/lib/thermal_subsystem.hpp
new file mode 100644
index 0000000..0d5b88b
--- /dev/null
+++ b/redfish-core/lib/thermal_subsystem.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "app.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/chassis_utils.hpp"
+#include "utils/json_utils.hpp"
+
+namespace redfish
+{
+
+inline void doThermalSubsystemCollection(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId,
+    const std::optional<std::string>& validChassisPath)
+{
+    if (!validChassisPath)
+    {
+        BMCWEB_LOG_ERROR << "Not a valid chassis ID" << chassisId;
+        messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
+        return;
+    }
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#ThermalSubsystem.v1_0_0.ThermalSubsystem";
+    asyncResp->res.jsonValue["Name"] = "Thermal Subsystem";
+    asyncResp->res.jsonValue["Id"] = "ThermalSubsystem";
+
+    asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Chassis", chassisId, "ThermalSubsystem");
+
+    asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+    asyncResp->res.jsonValue["Status"]["Health"] = "OK";
+}
+
+inline void handleThermalSubsystemCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& param)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    const std::string& chassisId = param;
+
+    redfish::chassis_utils::getValidChassisPath(
+        asyncResp, chassisId,
+        std::bind_front(doThermalSubsystemCollection, asyncResp, chassisId));
+}
+
+inline void requestRoutesThermalSubsystem(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/")
+        .privileges(redfish::privileges::getThermalSubsystem)
+        .methods(boost::beast::http::verb::get)(std::bind_front(
+            handleThermalSubsystemCollectionGet, std::ref(app)));
+}
+
+} // namespace redfish