Implements PowerSubsystem schema

This commit implements the Redfish PowerSubsystem schema and collects
default property values.
PowerSupplies will be implemented in the next commit.

ref:
https://www.dmtf.org/sites/default/files/standards/documents/
DSP0268_2022.2.pdf (6.86 PowerSubsystem 1.1.0)
https://redfish.dmtf.org/schemas/v1/PowerSupply.v1_1_0.json

Tested: Validator and UT passes
1. curl -k  -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem",
"@odata.type": "#PowerSubsystem.v1_1_0.PowerSubsystem",
"Id": "PowerSubsystem",
"Name": "Power Subsystem",
"Status": {
"Health": "OK",
"State": "Enabled"
}
}

2. bad chassisID
curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}
/redfish/v1/Chassis/badchassisID/PowerSubsystem/
PowerSupplies/powersupply0
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The requested resource of type Chassis named
                   badchassisID was not found.",
        "MessageArgs": [
          "Chassis",
          "badchassisID"
        ],
        "MessageId": "Base.1.13.1.ResourceNotFound",
        "MessageSeverity": "Critical",
        "Resolution": "Provide a valid resource identifier
                      and resubmit the request."
      }
    ],
    "code": "Base.1.13.1.ResourceNotFound",
    "message": "The requested resource of type Chassis named
               badchassisID was not found."
  }
}

Signed-off-by: Chicago Duan <duanzhijia01@inspur.com>
Change-Id: I6885b1777082538eceaf7ea85a8f69966459ee43
diff --git a/meson.build b/meson.build
index 3e45e82..8c86fa9 100644
--- a/meson.build
+++ b/meson.build
@@ -381,6 +381,7 @@
   'test/redfish-core/lib/log_services_dump_test.cpp',
   'test/redfish-core/lib/service_root_test.cpp',
   'test/redfish-core/lib/thermal_subsystem_test.cpp',
+  'test/redfish-core/lib/power_subsystem_test.cpp',
 )
 
 if(get_option('tests').enabled())
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 796df8d..b20944b 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -33,6 +33,7 @@
 #include "../lib/network_protocol.hpp"
 #include "../lib/pcie.hpp"
 #include "../lib/power.hpp"
+#include "../lib/power_subsystem.hpp"
 #include "../lib/processor.hpp"
 #include "../lib/redfish_sessions.hpp"
 #include "../lib/redfish_v1.hpp"
@@ -78,6 +79,7 @@
         requestRoutesPower(app);
 #endif
 #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM
+        requestRoutesPowerSubsystem(app);
         requestRoutesThermalSubsystem(app);
 #endif
         requestRoutesManagerCollection(app);
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index c51b150..2614115 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -403,6 +403,9 @@
                 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] =
                     crow::utility::urlFromPieces("redfish", "v1", "Chassis",
                                                  chassisId, "ThermalSubsystem");
+                asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] =
+                    crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                 chassisId, "PowerSubsystem");
 #endif
                 // SensorCollection
                 asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
diff --git a/redfish-core/lib/power_subsystem.hpp b/redfish-core/lib/power_subsystem.hpp
new file mode 100644
index 0000000..c3b898c
--- /dev/null
+++ b/redfish-core/lib/power_subsystem.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "app.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/chassis_utils.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+
+namespace redfish
+{
+
+inline void doPowerSubsystemCollection(
+    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"] =
+        "#PowerSubsystem.v1_1_0.PowerSubsystem";
+    asyncResp->res.jsonValue["Name"] = "Power Subsystem";
+    asyncResp->res.jsonValue["Id"] = "PowerSubsystem";
+    asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Chassis", chassisId, "PowerSubsystem");
+    asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+    asyncResp->res.jsonValue["Status"]["Health"] = "OK";
+
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/PowerSubsystem/PowerSubsystem.json>; rel=describedby");
+}
+
+inline void handlePowerSubsystemCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    redfish::chassis_utils::getValidChassisPath(
+        asyncResp, chassisId,
+        std::bind_front(doPowerSubsystemCollection, asyncResp, chassisId));
+}
+
+inline void requestRoutesPowerSubsystem(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/")
+        .privileges(redfish::privileges::getPowerSubsystem)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handlePowerSubsystemCollectionGet, std::ref(app)));
+}
+
+} // namespace redfish
diff --git a/test/redfish-core/lib/power_subsystem_test.cpp b/test/redfish-core/lib/power_subsystem_test.cpp
new file mode 100644
index 0000000..1dfbbeb
--- /dev/null
+++ b/test/redfish-core/lib/power_subsystem_test.cpp
@@ -0,0 +1,43 @@
+#include "async_resp.hpp"
+#include "power_subsystem.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <optional>
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+namespace
+{
+
+constexpr const char* chassisId = "ChassisId";
+constexpr const char* validChassisPath = "ChassisPath";
+
+void assertPowerSubsystemCollectionGet(crow::Response& res)
+{
+    nlohmann::json& json = res.jsonValue;
+    EXPECT_EQ(json["@odata.type"], "#PowerSubsystem.v1_1_0.PowerSubsystem");
+    EXPECT_EQ(json["Name"], "Power Subsystem");
+    EXPECT_EQ(json["Id"], "PowerSubsystem");
+    EXPECT_EQ(json["@odata.id"],
+              "/redfish/v1/Chassis/ChassisId/PowerSubsystem");
+    EXPECT_EQ(json["Status"]["State"], "Enabled");
+    EXPECT_EQ(json["Status"]["Health"], "OK");
+}
+
+TEST(PowerSubsystemCollectionTest,
+     PowerSubsystemCollectionStaticAttributesAreExpected)
+{
+    auto shareAsyncResp = std::make_shared<bmcweb::AsyncResp>();
+    shareAsyncResp->res.setCompleteRequestHandler(
+        assertPowerSubsystemCollectionGet);
+    doPowerSubsystemCollection(
+        shareAsyncResp, chassisId,
+        std::make_optional<std::string>(validChassisPath));
+}
+
+} // namespace
+} // namespace redfish