Activation: check compatiblity of uploaded software

Before activation, check if the PSU inventory's manufacturer and model
matches the uploaded software, to make sure the software is not updated
to a incompatible PSU.

The model check is mandatory, and if the PSU manufacturer is empty,
ignore the manufacturer check.

Tested: Upload a dummy tarball with incompatible model, verify the
        activation fails;
        Upload a dummy tarball with compatible model, verify the
        activation succeeds with a dummy update service.
        Also added unit tests for several cases:
        * Update on a PSU that model is incompatible;
        * Update on a PSU that the manufacture is incompatible;
        * Update on a PSU that the menufacture is empty;
        * Update on 4 PSUs that the second one is incompatible.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: Ia1b6a3fa6c98cdea1ea93c917c0938d4a60f0911
diff --git a/src/activation.cpp b/src/activation.cpp
index f1181fb..5023712 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -183,7 +183,21 @@
 
     for (const auto& p : psuPaths)
     {
-        psuQueue.push(p);
+        if (isCompatible(p))
+        {
+            psuQueue.push(p);
+        }
+        else
+        {
+            log<level::NOTICE>("PSU not compatible",
+                               entry("PSU=%s", p.c_str()));
+        }
+    }
+
+    if (psuQueue.empty())
+    {
+        log<level::ERR>("No PSU compatible with the software");
+        return Status::Failed;
     }
 
     // The progress to be increased for each successful update of PSU
@@ -257,6 +271,28 @@
     }
 }
 
+bool Activation::isCompatible(const std::string& psuInventoryPath)
+{
+    auto service =
+        utils::getService(bus, psuInventoryPath.c_str(), ASSET_IFACE);
+    auto psuManufacturer = utils::getProperty<std::string>(
+        bus, service.c_str(), psuInventoryPath.c_str(), ASSET_IFACE,
+        MANUFACTURER);
+    auto psuModel = utils::getProperty<std::string>(
+        bus, service.c_str(), psuInventoryPath.c_str(), ASSET_IFACE, MODEL);
+    if (psuModel != model)
+    {
+        // The model shall match
+        return false;
+    }
+    if (!psuManufacturer.empty())
+    {
+        // If PSU inventory has manufacturer property, it shall match
+        return psuManufacturer == manufacturer;
+    }
+    return true;
+}
+
 } // namespace updater
 } // namespace software
 } // namespace phosphor