Add Redfish EnvironmentMetrics schema in bmcweb

This commit implements Chassis' EnvironmentMetrics schema, a resource
in Redfish version 2022.2 that represents the environment metrics
implemented by Redfish.

This resource includes Energy consumption, Fan speeds (percent),
Power consumption (Watts), etc. And these data are mainly obtained
from under sensors.

Only the basic information of EnvironmentMetrics is implemented in
the current commit.

ref:
https://www.dmtf.org/sites/default/files/standards/documents/
DSP0268_2022.2.pdf (6.31 EnvironmentMetrics 1.3.0)
http://redfish.dmtf.org/schemas/v1/EnvironmentMetrics.v1_3_0.json

Tested: Validator passes
1. doGet method to get EnvironmentMetrics
curl -k -H "X-Auth-Token: $token" https://$bmc/redfish/v1/Chassis/
chassis/EnvironmentMetrics
{
"@odata.id": "/redfish/v1/Chassis/chassis/EnvironmentMetrics",
"@odata.type": "#EnvironmentMetrics.v1_3_0.EnvironmentMetrics",
"Id": "EnvironmentMetrics",
"Name": "Chassis Environment Metrics"
}

2. Input the wrong chassisId with the doGet method
curl -k -H "X-Auth-Token: $token" https://$bmc/redfish/v1/Chassis/
chassisError/EnvironmentMetrics
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "#Message.v1_1_1.Message",
        "Message": "The requested resource of type Chassis named
                    'chassisError' was not found.",
        "MessageArgs": [
          "Chassis",
          "chassisError"
        ],
        "MessageId": "Base.1.13.0.ResourceNotFound",
        "MessageSeverity": "Critical",
        "Resolution": "Provide a valid resource identifier and
                       resubmit the request."
      }
    ],
    "code": "Base.1.13.0.ResourceNotFound",
    "message": "The requested resource of type Chassis named
                'chassisError' was not found."
  }
}

Signed-off-by: Albert Zhang <zhanghaodi@inspur.com>
Change-Id: I6a75dfbc94f39d90a1368770076b874608394691
Signed-off-by: George Liu <liuxiwei@inspur.com>
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index 4c4a9c4..c6016cb 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -406,6 +406,10 @@
                 asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] =
                     crow::utility::urlFromPieces("redfish", "v1", "Chassis",
                                                  chassisId, "PowerSubsystem");
+                asyncResp->res.jsonValue["EnvironmentMetrics"]["@odata.id"] =
+                    crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+                                                 chassisId,
+                                                 "EnvironmentMetrics");
 #endif
                 // SensorCollection
                 asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
diff --git a/redfish-core/lib/environment_metrics.hpp b/redfish-core/lib/environment_metrics.hpp
new file mode 100644
index 0000000..f46ef34
--- /dev/null
+++ b/redfish-core/lib/environment_metrics.hpp
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "app.hpp"
+#include "utils/chassis_utils.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+
+namespace redfish
+{
+
+inline void handleEnvironmentMetricsHead(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    auto respHandler = [asyncResp, chassisId](
+                           const std::optional<std::string>& validChassisPath) {
+        if (!validChassisPath)
+        {
+            messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
+            return;
+        }
+
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby");
+    };
+
+    redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId,
+                                                std::move(respHandler));
+}
+
+inline void handleEnvironmentMetricsGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    auto respHandler = [asyncResp, chassisId](
+                           const std::optional<std::string>& validChassisPath) {
+        if (!validChassisPath)
+        {
+            messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
+            return;
+        }
+
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/EnvironmentMetrics/EnvironmentMetrics.json>; rel=describedby");
+        asyncResp->res.jsonValue["@odata.type"] =
+            "#EnvironmentMetrics.v1_3_0.EnvironmentMetrics";
+        asyncResp->res.jsonValue["Name"] = "Chassis Environment Metrics";
+        asyncResp->res.jsonValue["Id"] = "EnvironmentMetrics";
+        asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+            "redfish", "v1", "Chassis", chassisId, "EnvironmentMetrics");
+    };
+
+    redfish::chassis_utils::getValidChassisPath(asyncResp, chassisId,
+                                                std::move(respHandler));
+}
+
+inline void requestRoutesEnvironmentMetrics(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/")
+        .privileges(redfish::privileges::headEnvironmentMetrics)
+        .methods(boost::beast::http::verb::head)(
+            std::bind_front(handleEnvironmentMetricsHead, std::ref(app)));
+
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/EnvironmentMetrics/")
+        .privileges(redfish::privileges::getEnvironmentMetrics)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handleEnvironmentMetricsGet, std::ref(app)));
+}
+
+} // namespace redfish