inventory: implement deserialization

Use Cereal to deserialize inventory information persisted in the
filesystem and restore that to create inventory d-bus objects. Perform
the restore when the app starts.

Change-Id: I0f36a9cbdde223e4bfd9e178e33f5677217ba881
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/generated.mako.cpp b/generated.mako.cpp
index 7eeb8c4..6fc1521 100644
--- a/generated.mako.cpp
+++ b/generated.mako.cpp
@@ -31,7 +31,10 @@
                     ${i.namespace()}>>::assign,
             MakeInterface<
                 ServerObject<
-                    ${i.namespace()}>>::serialize
+                    ${i.namespace()}>>::serialize,
+            MakeInterface<
+                ServerObject<
+                    ${i.namespace()}>>::deserialize
         )
     },
 % endfor
diff --git a/manager.cpp b/manager.cpp
index 86b1757..6049161 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -18,6 +18,7 @@
 #include <chrono>
 #include <algorithm>
 #include <phosphor-logging/log.hpp>
+#include <experimental/filesystem>
 #include "manager.hpp"
 #include "errors.hpp"
 
@@ -103,6 +104,9 @@
     }
 
     _bus.request_name(busname);
+
+    // Restore any persistent inventory
+    restore();
 }
 
 void Manager::shutdown() noexcept
@@ -146,7 +150,8 @@
     const sdbusplus::message::object_path& path,
     const Object& interfaces,
     ObjectReferences::iterator pos,
-    bool newObject)
+    bool newObject,
+    bool restoreFromCache)
 {
     auto& refaces = pos->second;
     auto ifaceit = interfaces.cbegin();
@@ -200,8 +205,16 @@
                 auto& assign = std::get<AssignerType>(opsit->second);
                 assign(ifaceit->second, refaceit->second);
             }
-            auto& serialize = std::get<SerializerType>(opsit->second);
-            serialize(path, ifaceit->first, refaceit->second);
+            if (!restoreFromCache)
+            {
+                auto& serialize = std::get<SerializerType>(opsit->second);
+                serialize(path, ifaceit->first, refaceit->second);
+            }
+            else
+            {
+                auto& deserialize = std::get<DeserializerType>(opsit->second);
+                deserialize(path, ifaceit->first, refaceit->second);
+            }
         }
         catch (const InterfaceError& e)
         {
@@ -225,7 +238,8 @@
 }
 
 void Manager::updateObjects(
-    const std::map<sdbusplus::message::object_path, Object>& objs)
+    const std::map<sdbusplus::message::object_path, Object>& objs,
+    bool restoreFromCache)
 {
     auto objit = objs.cbegin();
     auto refit = _refs.begin();
@@ -255,7 +269,8 @@
             newObj = true;
         }
 
-        updateInterfaces(absPath, objit->second, refit, newObj);
+        updateInterfaces(absPath, objit->second, refit, newObj,
+                         restoreFromCache);
         ++objit;
     }
 }
@@ -331,6 +346,50 @@
     return iit->second;
 }
 
+void Manager::restore()
+{
+    namespace fs = std::experimental::filesystem;
+
+    if (!fs::exists(fs::path(PIM_PERSIST_PATH)))
+    {
+        return;
+    }
+
+    static const std::string remove =
+        std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT;
+
+    std::map<sdbusplus::message::object_path, Object> objects;
+    for (const auto& dirent :
+         fs::recursive_directory_iterator(PIM_PERSIST_PATH))
+    {
+        const auto& path = dirent.path();
+        if (fs::is_regular_file(path))
+        {
+            auto ifaceName = path.filename().string();
+            auto objPath = path.parent_path().string();
+            objPath.erase(0, remove.length());
+            auto objit = objects.find(objPath);
+            Interface propertyMap{};
+            if (objects.end() != objit)
+            {
+                auto& object = objit->second;
+                object.emplace(std::move(ifaceName), std::move(propertyMap));
+            }
+            else
+            {
+                Object object;
+                object.emplace(std::move(ifaceName), std::move(propertyMap));
+                objects.emplace(std::move(objPath), std::move(object));
+            }
+        }
+    }
+    if (!objects.empty())
+    {
+        auto restoreFromCache = true;
+        updateObjects(objects, restoreFromCache);
+    }
+}
+
 } // namespace manager
 } // namespace inventory
 } // namespace phosphor
diff --git a/manager.hpp b/manager.hpp
index a94d5ea..e6c60a3 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -94,6 +94,13 @@
             *any_ns::any_cast<const std::shared_ptr<T> &>(holder);
         cereal::serialize(path, iface, object);
     }
+
+    static void deserialize(const std::string& path, const std::string& iface,
+                            any_ns::any& holder)
+    {
+        auto& object = *any_ns::any_cast<std::shared_ptr<T> &>(holder);
+        cereal::deserialize(path, iface, object);
+    }
 };
 
 /** @class Manager
@@ -152,7 +159,11 @@
 
         /** @brief Add or update objects on DBus. */
         void updateObjects(
-            const std::map<sdbusplus::message::object_path, Object>& objs);
+            const std::map<sdbusplus::message::object_path, Object>& objs,
+            bool restoreFromCache = false);
+
+        /** @brief Restore persistent inventory items */
+        void restore();
 
         /** @brief Invoke an sdbusplus server binding method.
          *
@@ -201,8 +212,11 @@
                              decltype(MakeInterface<int>::assign) >;
         using SerializerType = std::add_pointer_t <
                                decltype(MakeInterface<int>::serialize) >;
+        using DeserializerType = std::add_pointer_t <
+                                 decltype(MakeInterface<int>::deserialize) >;
         using Makers = std::map<std::string,
-                           std::tuple<MakerType, AssignerType, SerializerType>>;
+                           std::tuple<MakerType, AssignerType,
+                                      SerializerType, DeserializerType>>;
 
         /** @brief Provides weak references to interface holders.
          *
@@ -249,7 +263,8 @@
             const sdbusplus::message::object_path& path,
             const Object& interfaces,
             ObjectReferences::iterator pos,
-            bool emitSignals = true);
+            bool emitSignals = true,
+            bool restoreFromCache = false);
 
         /** @brief Provided for testing only. */
         volatile bool _shutdown;
diff --git a/serialize.hpp b/serialize.hpp
index 281f61c..7c30b9c 100644
--- a/serialize.hpp
+++ b/serialize.hpp
@@ -31,4 +31,25 @@
     oarchive(object);
 }
 
+/** @brief Deserialize inventory item
+ *
+ *  @param[in] path - DBus object path
+ *  @param[in] iface - Inventory interface name
+ *  @param[in] object - Object to be serialized
+ */
+template <typename T>
+inline void deserialize(
+    const Path& path, const Interface& iface, T& object)
+{
+    fs::path p(PIM_PERSIST_PATH);
+    p /= path;
+    p /= iface;
+    if (fs::exists(p))
+    {
+        std::ifstream is(p, std::ios::in | std::ios::binary);
+        cereal::JSONInputArchive iarchive(is);
+        iarchive(object);
+    }
+}
+
 } // namespace cereal