bmcweb: Redfish away from json cache

In the original incarnation of bmcweb, route registration was done
automatically.  This has proved to be a terrible idea, wraught with
corner cases and issues.

The route registration is currently the only user of the
redfish::Node::json element.  Unfortunately, as written, this structure
consumes a lot of memory that's duplicated and not very useful.  From a
performance perspective, there is almost no difference between
rebuilding the structure for each GET request, and having the "cache"
that needs to be copied into the response and modified before it can be
useful.

In the programming tradeoffs for bmc, lower memory usage is more important
than latency, especially at these levels.

Change-Id: I785e8352123e5e886acf05cd59cb23648f93839d
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index c44c63f..8712710 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -766,16 +766,6 @@
     EthernetCollection(CrowApp &app) :
         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
     {
-        Node::json["@odata.type"] =
-            "#EthernetInterfaceCollection.EthernetInterfaceCollection";
-        Node::json["@odata.context"] =
-            "/redfish/v1/"
-            "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
-        Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/EthernetInterfaces";
-        Node::json["Name"] = "Ethernet Network Interface Collection";
-        Node::json["Description"] =
-            "Collection of EthernetInterfaces for this Manager";
-
         entityPrivileges = {
             {boost::beast::http::verb::get, {{"Login"}}},
             {boost::beast::http::verb::head, {{"Login"}}},
@@ -792,7 +782,17 @@
     void doGet(crow::Response &res, const crow::Request &req,
                const std::vector<std::string> &params) override
     {
-        res.jsonValue = Node::json;
+        res.jsonValue["@odata.type"] =
+            "#EthernetInterfaceCollection.EthernetInterfaceCollection";
+        res.jsonValue["@odata.context"] =
+            "/redfish/v1/"
+            "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
+        res.jsonValue["@odata.id"] =
+            "/redfish/v1/Managers/bmc/EthernetInterfaces";
+        res.jsonValue["Name"] = "Ethernet Network Interface Collection";
+        res.jsonValue["Description"] =
+            "Collection of EthernetInterfaces for this Manager";
+
         // Get eth interface list, and call the below callback for JSON
         // preparation
         getEthernetIfaceList(
@@ -837,13 +837,6 @@
         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
              std::string())
     {
-        Node::json["@odata.type"] =
-            "#EthernetInterface.v1_2_0.EthernetInterface";
-        Node::json["@odata.context"] =
-            "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
-        Node::json["Name"] = "Manager Ethernet Interface";
-        Node::json["Description"] = "Management Network Interface";
-
         entityPrivileges = {
             {boost::beast::http::verb::get, {{"Login"}}},
             {boost::beast::http::verb::head, {{"Login"}}},
@@ -1236,13 +1229,11 @@
         }
     }
 
-    nlohmann::json parseInterfaceData(
-        const std::string &iface_id, const EthernetInterfaceData &ethData,
+    void parseInterfaceData(
+        nlohmann::json &json_response, const std::string &iface_id,
+        const EthernetInterfaceData &ethData,
         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
     {
-        // Copy JSON object to avoid race condition
-        nlohmann::json json_response(Node::json);
-
         json_response["Id"] = iface_id;
         json_response["@odata.id"] =
             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
@@ -1285,8 +1276,6 @@
                 }
             }
         }
-
-        return json_response;
     }
 
     /**
@@ -1315,8 +1304,16 @@
                                                "EthernetInterface", iface_id);
                     return;
                 }
-                asyncResp->res.jsonValue =
-                    parseInterfaceData(iface_id, ethData, ipv4Data);
+                asyncResp->res.jsonValue["@odata.type"] =
+                    "#EthernetInterface.v1_2_0.EthernetInterface";
+                asyncResp->res.jsonValue["@odata.context"] =
+                    "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
+                asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
+                asyncResp->res.jsonValue["Description"] =
+                    "Management Network Interface";
+
+                parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
+                                   ipv4Data);
             });
     }
 
@@ -1355,8 +1352,8 @@
                     return;
                 }
 
-                asyncResp->res.jsonValue =
-                    parseInterfaceData(iface_id, ethData, ipv4Data);
+                parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
+                                   ipv4Data);
 
                 for (auto propertyIt : patchReq.items())
                 {
@@ -1414,21 +1411,11 @@
      * Default Constructor
      */
     template <typename CrowApp>
-    // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc.  This shall be generic, but requires to
-    // update GetSubroutes method
     VlanNetworkInterface(CrowApp &app) :
         Node(app,
-             "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>>/VLANs/"
-             "<str>",
+             "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
              std::string(), std::string())
     {
-        Node::json["@odata.type"] =
-            "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
-        Node::json["@odata.context"] =
-            "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
-        Node::json["Name"] = "VLAN Network Interface";
-
         entityPrivileges = {
             {boost::beast::http::verb::get, {{"Login"}}},
             {boost::beast::http::verb::head, {{"Login"}}},
@@ -1439,14 +1426,11 @@
     }
 
   private:
-    nlohmann::json parseInterfaceData(
-        const std::string &parent_iface_id, const std::string &iface_id,
-        const EthernetInterfaceData &ethData,
+    void parseInterfaceData(
+        nlohmann::json &json_response, const std::string &parent_iface_id,
+        const std::string &iface_id, const EthernetInterfaceData &ethData,
         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
     {
-        // Copy JSON object to avoid race condition
-        nlohmann::json json_response(Node::json);
-
         // Fill out obvious data...
         json_response["Id"] = iface_id;
         json_response["@odata.id"] =
@@ -1458,7 +1442,6 @@
         {
             json_response["VLANId"] = *ethData.vlan_id;
         }
-        return json_response;
     }
 
     bool verifyNames(crow::Response &res, const std::string &parent,
@@ -1497,6 +1480,11 @@
 
         const std::string &parent_iface_id = params[0];
         const std::string &iface_id = params[1];
+        res.jsonValue["@odata.type"] =
+            "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
+        res.jsonValue["@odata.context"] =
+            "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
+        res.jsonValue["Name"] = "VLAN Network Interface";
 
         if (!verifyNames(res, parent_iface_id, iface_id))
         {
@@ -1512,8 +1500,9 @@
                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
                 if (success && ethData.vlan_id)
                 {
-                    asyncResp->res.jsonValue = parseInterfaceData(
-                        parent_iface_id, iface_id, ethData, ipv4Data);
+                    parseInterfaceData(asyncResp->res.jsonValue,
+                                       parent_iface_id, iface_id, ethData,
+                                       ipv4Data);
                 }
                 else
                 {
@@ -1568,8 +1557,8 @@
                     return;
                 }
 
-                asyncResp->res.jsonValue = parseInterfaceData(
-                    parentIfaceId, ifaceId, ethData, ipv4Data);
+                parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
+                                   ifaceId, ethData, ipv4Data);
 
                 for (auto propertyIt : patchReq.items())
                 {
@@ -1626,8 +1615,8 @@
                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
                 if (success && ethData.vlan_id)
                 {
-                    asyncResp->res.jsonValue = parseInterfaceData(
-                        parentIfaceId, ifaceId, ethData, ipv4Data);
+                    parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
+                                       ifaceId, ethData, ipv4Data);
 
                     auto callback =
                         [asyncResp](const boost::system::error_code ec) {
@@ -1661,20 +1650,10 @@
 {
   public:
     template <typename CrowApp>
-    // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc.  This shall be generic, but requires to
-    // update GetSubroutes method
     VlanNetworkInterfaceCollection(CrowApp &app) :
         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
              std::string())
     {
-        Node::json["@odata.type"] =
-            "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
-        Node::json["@odata.context"] =
-            "/redfish/v1/$metadata"
-            "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
-        Node::json["Name"] = "VLAN Network Interface Collection";
-
         entityPrivileges = {
             {boost::beast::http::verb::get, {{"Login"}}},
             {boost::beast::http::verb::head, {{"Login"}}},
@@ -1713,7 +1692,15 @@
                     messages::internalError(asyncResp->res);
                     return;
                 }
-                asyncResp->res.jsonValue = Node::json;
+                asyncResp->res.jsonValue["@odata.type"] =
+                    "#VLanNetworkInterfaceCollection."
+                    "VLanNetworkInterfaceCollection";
+                asyncResp->res.jsonValue["@odata.context"] =
+                    "/redfish/v1/$metadata"
+                    "#VLanNetworkInterfaceCollection."
+                    "VLanNetworkInterfaceCollection";
+                asyncResp->res.jsonValue["Name"] =
+                    "VLAN Network Interface Collection";
 
                 nlohmann::json iface_array = nlohmann::json::array();
 
@@ -1759,8 +1746,8 @@
             return;
         }
 
-        auto vlanIdJson = json.find("VLANId");
-        if (vlanIdJson == json.end())
+        auto vlanIdJson = postReq.find("VLANId");
+        if (vlanIdJson == postReq.end())
         {
             messages::propertyMissing(asyncResp->res, "VLANId");
             return;