Update to Chassis 1.10 and add PCIeDeviceCollection support

v1.10 of Chassis adds a PCIeDeviceCollection.  This change adds
support for the PCIeDeviceCollection and references it from
Chassis.

Tested:
Passed the Redfish Service Validator.

Change-Id: If3bb75f4fa90a9df4a2a94a7c7e0bcaf37673723
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index 2a6dce7..d0e0531 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -147,6 +147,7 @@
         nodes.emplace_back(std::make_unique<TrustStoreCertificate>(app));
         nodes.emplace_back(std::make_unique<SystemPCIeFunctionCollection>(app));
         nodes.emplace_back(std::make_unique<SystemPCIeFunction>(app));
+        nodes.emplace_back(std::make_unique<SystemPCIeDeviceCollection>(app));
         nodes.emplace_back(std::make_unique<SystemPCIeDevice>(app));
 
         nodes.emplace_back(std::make_unique<SensorCollection>(app));
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index b08ab25..d8bc8f5 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -323,13 +323,16 @@
                     }
 
                     asyncResp->res.jsonValue["@odata.type"] =
-                        "#Chassis.v1_9_0.Chassis";
+                        "#Chassis.v1_10_0.Chassis";
                     asyncResp->res.jsonValue["@odata.id"] =
                         "/redfish/v1/Chassis/" + chassisId;
                     asyncResp->res.jsonValue["@odata.context"] =
                         "/redfish/v1/$metadata#Chassis.Chassis";
                     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
                     asyncResp->res.jsonValue["ChassisType"] = "RackMount";
+                    asyncResp->res.jsonValue["PCIeDevices"] = {
+                        {"@odata.id",
+                         "/redfish/v1/Systems/system/PCIeDevices"}};
 
                     const std::string &connectionName =
                         connectionNames[0].first;
@@ -392,7 +395,7 @@
 
                 // Couldn't find an object with that name.  return an error
                 messages::resourceNotFound(
-                    asyncResp->res, "#Chassis.v1_9_0.Chassis", chassisId);
+                    asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId);
             },
             "xyz.openbmc_project.ObjectMapper",
             "/xyz/openbmc_project/object_mapper",
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 64ccdb2..36248a1 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -28,41 +28,40 @@
 static constexpr char const *pcieDeviceInterface =
     "xyz.openbmc_project.PCIe.Device";
 
-static inline void getPCIeDeviceList(std::shared_ptr<AsyncResp> asyncResp)
+static inline void getPCIeDeviceList(std::shared_ptr<AsyncResp> asyncResp,
+                                     const std::string &name)
 {
-    auto getPCIeMapCallback =
-        [asyncResp](const boost::system::error_code ec,
-                    std::vector<std::string> &pcieDevicePaths) {
-            if (ec)
+    auto getPCIeMapCallback = [asyncResp, name](
+                                  const boost::system::error_code ec,
+                                  std::vector<std::string> &pcieDevicePaths) {
+        if (ec)
+        {
+            BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: "
+                             << ec.message();
+            // Not an error, system just doesn't have PCIe info
+            return;
+        }
+        nlohmann::json &pcieDeviceList = asyncResp->res.jsonValue[name];
+        pcieDeviceList = nlohmann::json::array();
+        for (const std::string &pcieDevicePath : pcieDevicePaths)
+        {
+            size_t devStart = pcieDevicePath.rfind("/");
+            if (devStart == std::string::npos)
             {
-                BMCWEB_LOG_DEBUG << "no PCIe device paths found ec: "
-                                 << ec.message();
-                // Not an error, system just doesn't have PCIe info
-                return;
+                continue;
             }
-            nlohmann::json &pcieDeviceList =
-                asyncResp->res.jsonValue["PCIeDevices"];
-            pcieDeviceList = nlohmann::json::array();
-            for (const std::string &pcieDevicePath : pcieDevicePaths)
-            {
-                size_t devStart = pcieDevicePath.rfind("/");
-                if (devStart == std::string::npos)
-                {
-                    continue;
-                }
 
-                std::string devName = pcieDevicePath.substr(devStart + 1);
-                if (devName.empty())
-                {
-                    continue;
-                }
-                pcieDeviceList.push_back(
-                    {{"@odata.id",
-                      "/redfish/v1/Systems/system/PCIeDevices/" + devName}});
+            std::string devName = pcieDevicePath.substr(devStart + 1);
+            if (devName.empty())
+            {
+                continue;
             }
-            asyncResp->res.jsonValue["PCIeDevices@odata.count"] =
-                pcieDeviceList.size();
-        };
+            pcieDeviceList.push_back(
+                {{"@odata.id",
+                  "/redfish/v1/Systems/system/PCIeDevices/" + devName}});
+        }
+        asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
+    };
     crow::connections::systemBus->async_method_call(
         std::move(getPCIeMapCallback), "xyz.openbmc_project.ObjectMapper",
         "/xyz/openbmc_project/object_mapper",
@@ -70,6 +69,44 @@
         std::string(pciePath) + "/", 1, std::array<std::string, 0>());
 }
 
+class SystemPCIeDeviceCollection : public Node
+{
+  public:
+    template <typename CrowApp>
+    SystemPCIeDeviceCollection(CrowApp &app) :
+        Node(app, "/redfish/v1/Systems/system/PCIeDevices/")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+        asyncResp->res.jsonValue = {
+            {"@odata.type", "#PCIeDeviceCollection.PCIeDeviceCollection"},
+            {"@odata.context",
+             "/redfish/v1/"
+             "$metadata#PCIeDeviceCollection.PCIeDeviceCollection"},
+            {"@odata.id", "/redfish/v1/Systems/system/PCIeDevices"},
+            {"Name", "PCIe Device Collection"},
+            {"Description", "Collection of PCIe Devices"},
+            {"Members", nlohmann::json::array()},
+            {"Members@odata.count", 0}};
+        getPCIeDeviceList(asyncResp, "Members");
+    }
+};
+
 class SystemPCIeDevice : public Node
 {
   public:
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 0e2e52d..70c2eb8 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1666,7 +1666,7 @@
         getComputerSystem(asyncResp, health);
         getHostState(asyncResp);
         getBootProperties(asyncResp);
-        getPCIeDeviceList(asyncResp);
+        getPCIeDeviceList(asyncResp, "PCIeDevices");
         getHostWatchdogTimer(asyncResp);
     }