cpudimm.hpp: Separate Memory and Processor functionality

To match other files move Redfish Memory classes and functions
to a separate memory.hpp file. This naming "memory" (after the Redfish
schemas) matches other filenames. Do the same with Processor
classes and functions, moving to a separate processor.hpp.

cpudimm.hpp had grown to 1300+ lines. Makes development and review
easier.

Tested: Validator passes. Resources look the same.
Change-Id: I7e23ecaf9b4b69cc72aad6d94ad3a555ee76b28a
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp
new file mode 100644
index 0000000..5c98684
--- /dev/null
+++ b/redfish-core/lib/processor.hpp
@@ -0,0 +1,527 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+
+#include "health.hpp"
+
+#include <boost/container/flat_map.hpp>
+#include <node.hpp>
+#include <utils/collection.hpp>
+#include <utils/json_utils.hpp>
+
+namespace redfish
+{
+
+using InterfacesProperties = boost::container::flat_map<
+    std::string,
+    boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
+
+inline void
+    getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp,
+                          const InterfacesProperties& cpuInterfacesProperties)
+{
+    BMCWEB_LOG_DEBUG << "Get CPU resources by interface.";
+
+    // Added for future purpose. Once present and functional attributes added
+    // in busctl call, need to add actual logic to fetch original values.
+    bool present = false;
+    const bool functional = true;
+    auto health = std::make_shared<HealthPopulate>(aResp);
+    health->populate();
+
+    for (const auto& interface : cpuInterfacesProperties)
+    {
+        for (const auto& property : interface.second)
+        {
+            if (property.first == "CoreCount")
+            {
+                const uint16_t* coresCount =
+                    std::get_if<uint16_t>(&property.second);
+                if (coresCount == nullptr)
+                {
+                    // Important property not in desired type
+                    messages::internalError(aResp->res);
+                    return;
+                }
+                if (*coresCount == 0)
+                {
+                    // Slot is not populated, set status end return
+                    aResp->res.jsonValue["Status"]["State"] = "Absent";
+                    // HTTP Code will be set up automatically, just return
+                    return;
+                }
+                aResp->res.jsonValue["Status"]["State"] = "Enabled";
+                present = true;
+                aResp->res.jsonValue["TotalCores"] = *coresCount;
+            }
+            else if (property.first == "Socket")
+            {
+                const std::string* value =
+                    std::get_if<std::string>(&property.second);
+                if (value != nullptr)
+                {
+                    aResp->res.jsonValue["Socket"] = *value;
+                }
+            }
+            else if (property.first == "ThreadCount")
+            {
+                const int64_t* value = std::get_if<int64_t>(&property.second);
+                if (value != nullptr)
+                {
+                    aResp->res.jsonValue["TotalThreads"] = *value;
+                }
+            }
+            else if (property.first == "Family")
+            {
+                const std::string* value =
+                    std::get_if<std::string>(&property.second);
+                if (value != nullptr)
+                {
+                    aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] =
+                        *value;
+                }
+            }
+            else if (property.first == "Id")
+            {
+                const uint64_t* value = std::get_if<uint64_t>(&property.second);
+                if (value != nullptr && *value != 0)
+                {
+                    present = true;
+                    aResp->res
+                        .jsonValue["ProcessorId"]["IdentificationRegisters"] =
+                        boost::lexical_cast<std::string>(*value);
+                }
+            }
+        }
+    }
+
+    if (present == false)
+    {
+        aResp->res.jsonValue["Status"]["State"] = "Absent";
+        aResp->res.jsonValue["Status"]["Health"] = "OK";
+    }
+    else
+    {
+        aResp->res.jsonValue["Status"]["State"] = "Enabled";
+        if (functional)
+        {
+            aResp->res.jsonValue["Status"]["Health"] = "OK";
+        }
+        else
+        {
+            aResp->res.jsonValue["Status"]["Health"] = "Critical";
+        }
+    }
+
+    return;
+}
+
+inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp,
+                                const std::string& cpuId,
+                                const std::string& service,
+                                const std::string& objPath)
+{
+    BMCWEB_LOG_DEBUG << "Get available system cpu resources by service.";
+
+    crow::connections::systemBus->async_method_call(
+        [cpuId, service, objPath, aResp{std::move(aResp)}](
+            const boost::system::error_code ec,
+            const dbus::utility::ManagedObjectType& dbusData) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "DBUS response error";
+                messages::internalError(aResp->res);
+                return;
+            }
+            aResp->res.jsonValue["Id"] = cpuId;
+            aResp->res.jsonValue["Name"] = "Processor";
+            aResp->res.jsonValue["ProcessorType"] = "CPU";
+
+            bool slotPresent = false;
+            std::string corePath = objPath + "/core";
+            size_t totalCores = 0;
+            for (const auto& object : dbusData)
+            {
+                if (object.first.str == objPath)
+                {
+                    getCpuDataByInterface(aResp, object.second);
+                }
+                else if (boost::starts_with(object.first.str, corePath))
+                {
+                    for (const auto& interface : object.second)
+                    {
+                        if (interface.first ==
+                            "xyz.openbmc_project.Inventory.Item")
+                        {
+                            for (const auto& property : interface.second)
+                            {
+                                if (property.first == "Present")
+                                {
+                                    const bool* present =
+                                        std::get_if<bool>(&property.second);
+                                    if (present != nullptr)
+                                    {
+                                        if (*present == true)
+                                        {
+                                            slotPresent = true;
+                                            totalCores++;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            // In getCpuDataByInterface(), state and health are set
+            // based on the present and functional status. If core
+            // count is zero, then it has a higher precedence.
+            if (slotPresent)
+            {
+                if (totalCores == 0)
+                {
+                    // Slot is not populated, set status end return
+                    aResp->res.jsonValue["Status"]["State"] = "Absent";
+                    aResp->res.jsonValue["Status"]["Health"] = "OK";
+                }
+                aResp->res.jsonValue["TotalCores"] = totalCores;
+            }
+            return;
+        },
+        service, "/xyz/openbmc_project/inventory",
+        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+}
+
+inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp,
+                            const std::string& service,
+                            const std::string& objPath)
+{
+    BMCWEB_LOG_DEBUG << "Get Cpu Asset Data";
+    crow::connections::systemBus->async_method_call(
+        [objPath, aResp{std::move(aResp)}](
+            const boost::system::error_code ec,
+            const boost::container::flat_map<
+                std::string, std::variant<std::string, uint32_t, uint16_t,
+                                          bool>>& properties) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "DBUS response error";
+                messages::internalError(aResp->res);
+                return;
+            }
+
+            for (const auto& property : properties)
+            {
+                if (property.first == "SerialNumber")
+                {
+                    const std::string* sn =
+                        std::get_if<std::string>(&property.second);
+                    if (sn != nullptr && !sn->empty())
+                    {
+                        aResp->res.jsonValue["SerialNumber"] = *sn;
+                    }
+                }
+                else if (property.first == "Model")
+                {
+                    const std::string* model =
+                        std::get_if<std::string>(&property.second);
+                    if (model != nullptr && !model->empty())
+                    {
+                        aResp->res.jsonValue["Model"] = *model;
+                    }
+                }
+                else if (property.first == "Manufacturer")
+                {
+
+                    const std::string* mfg =
+                        std::get_if<std::string>(&property.second);
+                    if (mfg != nullptr)
+                    {
+                        aResp->res.jsonValue["Manufacturer"] = *mfg;
+
+                        // Otherwise would be unexpected.
+                        if (mfg->find("Intel") != std::string::npos)
+                        {
+                            aResp->res.jsonValue["ProcessorArchitecture"] =
+                                "x86";
+                            aResp->res.jsonValue["InstructionSet"] = "x86-64";
+                        }
+                        else if (mfg->find("IBM") != std::string::npos)
+                        {
+                            aResp->res.jsonValue["ProcessorArchitecture"] =
+                                "Power";
+                            aResp->res.jsonValue["InstructionSet"] = "PowerISA";
+                        }
+                    }
+                }
+            }
+        },
+        service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
+        "xyz.openbmc_project.Inventory.Decorator.Asset");
+}
+
+inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp,
+                               const std::string& service,
+                               const std::string& objPath)
+{
+    BMCWEB_LOG_DEBUG << "Get Cpu Revision Data";
+    crow::connections::systemBus->async_method_call(
+        [objPath, aResp{std::move(aResp)}](
+            const boost::system::error_code ec,
+            const boost::container::flat_map<
+                std::string, std::variant<std::string, uint32_t, uint16_t,
+                                          bool>>& properties) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "DBUS response error";
+                messages::internalError(aResp->res);
+                return;
+            }
+
+            for (const auto& property : properties)
+            {
+                if (property.first == "Version")
+                {
+                    const std::string* ver =
+                        std::get_if<std::string>(&property.second);
+                    if (ver != nullptr)
+                    {
+                        aResp->res.jsonValue["Version"] = *ver;
+                    }
+                    break;
+                }
+            }
+        },
+        service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
+        "xyz.openbmc_project.Inventory.Decorator.Revision");
+}
+
+inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp,
+                                        const std::string& acclrtrId,
+                                        const std::string& service,
+                                        const std::string& objPath)
+{
+    BMCWEB_LOG_DEBUG
+        << "Get available system Accelerator resources by service.";
+    crow::connections::systemBus->async_method_call(
+        [acclrtrId, aResp{std::move(aResp)}](
+            const boost::system::error_code ec,
+            const boost::container::flat_map<
+                std::string, std::variant<std::string, uint32_t, uint16_t,
+                                          bool>>& properties) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "DBUS response error";
+                messages::internalError(aResp->res);
+                return;
+            }
+            aResp->res.jsonValue["Id"] = acclrtrId;
+            aResp->res.jsonValue["Name"] = "Processor";
+            const bool* accPresent = nullptr;
+            const bool* accFunctional = nullptr;
+
+            for (const auto& property : properties)
+            {
+                if (property.first == "Functional")
+                {
+                    accFunctional = std::get_if<bool>(&property.second);
+                }
+                else if (property.first == "Present")
+                {
+                    accPresent = std::get_if<bool>(&property.second);
+                }
+            }
+
+            std::string state = "Enabled";
+            std::string health = "OK";
+
+            if (accPresent != nullptr && *accPresent == false)
+            {
+                state = "Absent";
+            }
+
+            if ((accFunctional != nullptr) && (*accFunctional == false))
+            {
+                if (state == "Enabled")
+                {
+                    health = "Critical";
+                }
+            }
+
+            aResp->res.jsonValue["Status"]["State"] = state;
+            aResp->res.jsonValue["Status"]["Health"] = health;
+            aResp->res.jsonValue["ProcessorType"] = "Accelerator";
+        },
+        service, objPath, "org.freedesktop.DBus.Properties", "GetAll", "");
+}
+
+inline void getProcessorData(std::shared_ptr<AsyncResp> aResp,
+                             const std::string& processorId,
+                             const std::vector<const char*>& inventoryItems)
+{
+    BMCWEB_LOG_DEBUG << "Get available system processor resources.";
+
+    crow::connections::systemBus->async_method_call(
+        [processorId, aResp{std::move(aResp)}](
+            const boost::system::error_code ec,
+            const boost::container::flat_map<
+                std::string, boost::container::flat_map<
+                                 std::string, std::vector<std::string>>>&
+                subtree) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "DBUS response error";
+                messages::internalError(aResp->res);
+                return;
+            }
+            for (const auto& object : subtree)
+            {
+                if (boost::ends_with(object.first, processorId))
+                {
+                    for (const auto& service : object.second)
+                    {
+                        for (const auto& inventory : service.second)
+                        {
+                            if (inventory == "xyz.openbmc_project."
+                                             "Inventory.Decorator.Asset")
+                            {
+                                getCpuAssetData(aResp, service.first,
+                                                object.first);
+                            }
+                            else if (inventory ==
+                                     "xyz.openbmc_project."
+                                     "Inventory.Decorator.Revision")
+                            {
+                                getCpuRevisionData(aResp, service.first,
+                                                   object.first);
+                            }
+                            else if (inventory == "xyz.openbmc_project."
+                                                  "Inventory.Item.Cpu")
+                            {
+                                getCpuDataByService(aResp, processorId,
+                                                    service.first,
+                                                    object.first);
+                            }
+                            else if (inventory == "xyz.openbmc_project."
+                                                  "Inventory.Item.Accelerator")
+                            {
+                                getAcceleratorDataByService(aResp, processorId,
+                                                            service.first,
+                                                            object.first);
+                            }
+                        }
+                    }
+                    return;
+                }
+            }
+            // Object not found
+            messages::resourceNotFound(aResp->res, "Processor", processorId);
+            return;
+        },
+        "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+        "/xyz/openbmc_project/inventory", 0, inventoryItems);
+}
+
+class ProcessorCollection : public Node
+{
+  public:
+    /*
+     * Default Constructor
+     */
+    ProcessorCollection(App& app) :
+        Node(app, "/redfish/v1/Systems/system/Processors/")
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response& res, const crow::Request&,
+               const std::vector<std::string>&) override
+    {
+        res.jsonValue["@odata.type"] =
+            "#ProcessorCollection.ProcessorCollection";
+        res.jsonValue["Name"] = "Processor Collection";
+
+        res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors/";
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+
+        collection_util::getResourceList(
+            asyncResp, "Processors",
+            {"xyz.openbmc_project.Inventory.Item.Cpu",
+             "xyz.openbmc_project.Inventory.Item.Accelerator"});
+    }
+};
+
+class Processor : public Node
+{
+  public:
+    /*
+     * Default Constructor
+     */
+    Processor(App& app) :
+        Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string())
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+            {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+    }
+
+  private:
+    /**
+     * Functions triggers appropriate requests on DBus
+     */
+    void doGet(crow::Response& res, const crow::Request&,
+               const std::vector<std::string>& params) override
+    {
+        // Check if there is required param, truly entering this shall be
+        // impossible
+        if (params.size() != 1)
+        {
+            messages::internalError(res);
+
+            res.end();
+            return;
+        }
+        const std::string& processorId = params[0];
+        res.jsonValue["@odata.type"] = "#Processor.v1_9_0.Processor";
+        res.jsonValue["@odata.id"] =
+            "/redfish/v1/Systems/system/Processors/" + processorId;
+
+        auto asyncResp = std::make_shared<AsyncResp>(res);
+
+        getProcessorData(asyncResp, processorId,
+                         {"xyz.openbmc_project.Inventory.Item.Cpu",
+                          "xyz.openbmc_project.Inventory.Decorator.Asset",
+                          "xyz.openbmc_project.Inventory.Item.Accelerator"});
+    }
+};
+
+} // namespace redfish