tools: add update handler
Add an UpdateHandler to handle each step of the update process.
This code was already in place but is now handled via an UpdateHandler
object.
Tested: Not yet tested.
Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I36cd1b94d8e2c0788d09805d935738d86e6e33de
diff --git a/tools/main.cpp b/tools/main.cpp
index 5868d11..536cde1 100644
--- a/tools/main.cpp
+++ b/tools/main.cpp
@@ -175,8 +175,8 @@
/* The parameters are all filled out. */
try
{
- host_tool::updaterMain(&blob, handler.get(), imagePath,
- signaturePath);
+ host_tool::UpdateHandler updater(&blob, handler.get());
+ host_tool::updaterMain(&updater, imagePath, signaturePath);
}
catch (const host_tool::ToolException& e)
{
diff --git a/tools/updater.cpp b/tools/updater.cpp
index 975ee0f..92afe5d 100644
--- a/tools/updater.cpp
+++ b/tools/updater.cpp
@@ -26,10 +26,84 @@
#include <memory>
#include <string>
#include <thread>
+#include <vector>
namespace host_tool
{
+bool UpdateHandler::checkAvailable(const std::string& goalFirmware)
+{
+ std::vector<std::string> blobs = blob->getBlobList();
+
+ auto blobInst = std::find_if(
+ blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
+ /* Running into weird scenarios where the string comparison doesn't
+ * work. TODO: revisit.
+ */
+ return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
+ goalFirmware.length()));
+ // return (goalFirmware.compare(iter));
+ });
+ if (blobInst == blobs.end())
+ {
+ std::fprintf(stderr, "%s not found\n", goalFirmware.c_str());
+ return false;
+ }
+
+ /* Call stat on /flash/image (or /flash/tarball) and check if data interface
+ * is supported.
+ */
+ ipmiblob::StatResponse stat;
+
+ try
+ {
+ stat = blob->getStat(goalFirmware);
+ }
+ catch (const ipmiblob::BlobException& b)
+ {
+ std::fprintf(stderr, "Received exception '%s' on getStat\n", b.what());
+ return false;
+ }
+
+ auto supported = handler->supportedType();
+ if ((stat.blob_state & supported) == 0)
+ {
+ std::fprintf(stderr, "data interface selected not supported.\n");
+ return false;
+ }
+
+ return true;
+}
+
+void UpdateHandler::sendFile(const std::string& target, const std::string& path)
+{
+ std::uint16_t session;
+ auto supported = handler->supportedType();
+
+ try
+ {
+ session = blob->openBlob(
+ target, static_cast<std::uint16_t>(supported) |
+ static_cast<std::uint16_t>(blobs::OpenFlags::write));
+ }
+ catch (const ipmiblob::BlobException& b)
+ {
+ throw ToolException("blob exception received: " +
+ std::string(b.what()));
+ }
+
+ if (!handler->sendContents(path, session))
+ {
+ /* Need to close the session on failure, or it's stuck open (until the
+ * blob handler timeout is implemented, and even then, why make it wait.
+ */
+ blob->closeBlob(session);
+ throw ToolException("Failed to send contents of " + path);
+ }
+
+ blob->closeBlob(session);
+}
+
/* Poll an open verification session. Handling closing the session is not yet
* owned by this method. */
bool pollVerificationStatus(std::uint16_t session,
@@ -111,120 +185,15 @@
blobs::FirmwareBlobHandler::VerifyCheckResponses::success);
}
-void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler,
- const std::string& imagePath, const std::string& signaturePath)
+bool UpdateHandler::verifyFile(const std::string& target)
{
- /* TODO(venture): Add optional parameter to specify the flash type, default
- * to legacy for now.
- *
- * TODO(venture): Move the strings from the FirmwareHandler object to a
- * boring utils object so it will be more happly linked cleanly to both the
- * BMC and host-side.
- */
- std::string goalFirmware = "/flash/image";
- std::string hashFilename = "/flash/hash";
- std::string verifyFilename = "/flash/verify";
-
- /* Get list of blob_ids, check for /flash/image, or /flash/tarball.
- * TODO(venture) the mechanism doesn't care, but the caller of burn_my_bmc
- * will have in mind which they're sending and we need to verify it's
- * available and use it.
- */
- std::vector<std::string> blobs = blob->getBlobList();
- auto blobInst = std::find_if(
- blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
- /* Running into weird scenarios where the string comparison doesn't
- * work. TODO: revisit.
- */
- return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
- goalFirmware.length()));
- // return (goalFirmware.compare(iter));
- });
- if (blobInst == blobs.end())
- {
- throw ToolException(goalFirmware + " not found");
- }
-
- /* Call stat on /flash/image (or /flash/tarball) and check if data interface
- * is supported.
- */
- ipmiblob::StatResponse stat;
- try
- {
- stat = blob->getStat(goalFirmware);
- }
- catch (const ipmiblob::BlobException& b)
- {
- throw ToolException("blob exception received: " +
- std::string(b.what()));
- }
-
- auto supported = handler->supportedType();
- if ((stat.blob_state & supported) == 0)
- {
- throw ToolException("data interface selected not supported.");
- }
-
- /* Yay, our data handler is supported. */
-
- /* Send over the firmware image. */
- std::fprintf(stderr, "Sending over the firmware image.\n");
std::uint16_t session;
+ bool success = false;
+
try
{
session = blob->openBlob(
- goalFirmware,
- static_cast<std::uint16_t>(supported) |
- static_cast<std::uint16_t>(blobs::OpenFlags::write));
- }
- catch (const ipmiblob::BlobException& b)
- {
- throw ToolException("blob exception received: " +
- std::string(b.what()));
- }
-
- if (!handler->sendContents(imagePath, session))
- {
- /* Need to close the session on failure, or it's stuck open (until the
- * blob handler timeout is implemented, and even then, why make it wait.
- */
- blob->closeBlob(session);
- throw ToolException("Failed to send contents of " + imagePath);
- }
-
- blob->closeBlob(session);
-
- /* Send over the hash contents. */
- std::fprintf(stderr, "Sending over the hash file.\n");
- try
- {
- session = blob->openBlob(
- hashFilename,
- static_cast<std::uint16_t>(supported) |
- static_cast<std::uint16_t>(blobs::OpenFlags::write));
- }
- catch (const ipmiblob::BlobException& b)
- {
- throw ToolException("blob exception received: " +
- std::string(b.what()));
- }
-
- if (!handler->sendContents(signaturePath, session))
- {
- blob->closeBlob(session);
- throw ToolException("Failed to send contents of " + signaturePath);
- }
-
- blob->closeBlob(session);
-
- /* Trigger the verification by opening the verify file. */
- std::fprintf(stderr, "Opening the verification file\n");
- try
- {
- session = blob->openBlob(
- verifyFilename,
- static_cast<std::uint16_t>(supported) |
- static_cast<std::uint16_t>(blobs::OpenFlags::write));
+ target, static_cast<std::uint16_t>(blobs::OpenFlags::write));
}
catch (const ipmiblob::BlobException& b)
{
@@ -235,6 +204,7 @@
std::fprintf(
stderr,
"Committing to verification file to trigger verification service\n");
+
try
{
blob->commit(session, {});
@@ -251,6 +221,7 @@
if (pollVerificationStatus(session, blob))
{
std::fprintf(stderr, "Verification returned success\n");
+ success = true;
}
else
{
@@ -258,12 +229,50 @@
"be running (unlikely))\n");
}
- /* DO NOT CLOSE the verification session until it's done.
- * TODO: Evaluate what closing verification should do? If the process is
- * complete, nothing bad, maybe reset the entire state machine? This will
- * benefit from a diagram.
- */
blob->closeBlob(session);
+ return (success == true);
+}
+
+void updaterMain(UpdateHandler* updater, const std::string& imagePath,
+ const std::string& signaturePath)
+{
+ /* TODO(venture): Add optional parameter to specify the flash type, default
+ * to legacy for now.
+ *
+ * TODO(venture): Move the strings from the FirmwareHandler object to a
+ * boring utils object so it will be more happly linked cleanly to both the
+ * BMC and host-side.
+ */
+ std::string goalFirmware = "/flash/image";
+ std::string hashFilename = "/flash/hash";
+ std::string verifyFilename = "/flash/verify";
+
+ bool goalSupported = updater->checkAvailable(goalFirmware);
+ if (!goalSupported)
+ {
+ throw ToolException("Goal firmware or interface not supported");
+ }
+
+ /* Yay, our data handler is supported. */
+
+ /* Send over the firmware image. */
+ std::fprintf(stderr, "Sending over the firmware image.\n");
+ updater->sendFile(goalFirmware, imagePath);
+
+ /* Send over the hash contents. */
+ std::fprintf(stderr, "Sending over the hash file.\n");
+ updater->sendFile(hashFilename, signaturePath);
+
+ /* Trigger the verification by opening the verify file. */
+ std::fprintf(stderr, "Opening the verification file\n");
+ if (updater->verifyFile(verifyFilename))
+ {
+ std::fprintf(stderr, "succeeded\n");
+ }
+ else
+ {
+ std::fprintf(stderr, "failed\n");
+ }
}
} // namespace host_tool
diff --git a/tools/updater.hpp b/tools/updater.hpp
index 062f704..4674e91 100644
--- a/tools/updater.hpp
+++ b/tools/updater.hpp
@@ -8,6 +8,50 @@
namespace host_tool
{
+/** Object that actually handles the update itself. */
+class UpdateHandler
+{
+ public:
+ UpdateHandler(ipmiblob::BlobInterface* blob, DataInterface* handler) :
+ blob(blob), handler(handler)
+ {
+ }
+
+ virtual ~UpdateHandler() = default;
+
+ /**
+ * Check if the goal firmware is listed in the blob_list and that the
+ * handler's supported data type is available.
+ *
+ * @param[in] goalFirmware - the firmware to check /flash/image
+ * /flash/tarball, etc.
+ */
+ virtual bool checkAvailable(const std::string& goalFirmware);
+
+ /**
+ * Send the file contents at path to the blob id, target.
+ *
+ * @param[in] target - the blob id
+ * @param[in] path - the source file path
+ * @throw ToolException on failure.
+ */
+ virtual void sendFile(const std::string& target, const std::string& path);
+
+ /**
+ * Trigger verification.
+ *
+ * @param[in] target - the verification blob id (may support multiple in the
+ * future.
+ * @return true if verified, false if verification errors.
+ * @throw ToolException on failure (TODO: throw on timeout.)
+ */
+ virtual bool verifyFile(const std::string& target);
+
+ private:
+ ipmiblob::BlobInterface* blob;
+ DataInterface* handler;
+};
+
/**
* Poll an open verification session.
*
@@ -21,14 +65,12 @@
/**
* Attempt to update the BMC's firmware using the interface provided.
*
- * @param[in] blob - pointer to blob interface implementation object.
- * @param[in] handler - pointer to the data interface implementation object.
+ * @param[in] updater - update handler object.
* @param[in] imagePath - the path to the image file.
* @param[in] signaturePath - the path to the signature file.
* @throws ToolException on failures.
*/
-void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler,
- const std::string& imagePath,
+void updaterMain(UpdateHandler* updater, const std::string& imagePath,
const std::string& signaturePath);
} // namespace host_tool