Skip to content

Commit

Permalink
msl_verify: Initial commit
Browse files Browse the repository at this point in the history
Create a new binary that checks the msl (minimum ship level)
of the PNOR and logs an error message if the version on the
system is older. The msl can be specified via a config flag.

Change-Id: I6f477400f7a8cf56557bd0caf5d6e08d73320028
Signed-off-by: Adriana Kobylak <[email protected]>
  • Loading branch information
anoo1 committed Oct 17, 2018
1 parent 139604e commit 4772a94
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 2 deletions.
9 changes: 8 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ AM_DEFAULT_SOURCE_EXT = .cpp
ACLOCAL_AMFLAGS = -Im4

sbin_PROGRAMS = \
openpower-update-manager
openpower-update-manager \
openpower-pnor-msl

openpower_update_manager_SOURCES = \
activation.cpp \
Expand All @@ -22,6 +23,10 @@ endif
nodist_openpower_update_manager_SOURCES = \
org/openbmc/Associations/server.cpp

openpower_pnor_msl_SOURCES = \
msl_verify.cpp \
msl_verify_main.cpp

CLEANFILES = \
org/openbmc/Associations/server.cpp \
org/openbmc/Associations/server.hpp
Expand Down Expand Up @@ -55,5 +60,7 @@ org/openbmc/Associations/server.hpp: org/openbmc/Associations.interface.yaml

openpower_update_manager_CXXFLAGS = $(generic_cxxflags)
openpower_update_manager_LDFLAGS = $(generic_ldflags)
openpower_pnor_msl_CXXFLAGS = $(generic_cxxflags)
openpower_pnor_msl_LDFLAGS = $(generic_ldflags)

SUBDIRS = test
9 changes: 9 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,14 @@ AC_ARG_VAR(ACTIVE_PNOR_MAX_ALLOWED, [The maximum allowed active pnor versions])
AS_IF([test "x$ACTIVE_PNOR_MAX_ALLOWED" == "x"], [ACTIVE_PNOR_MAX_ALLOWED=2])
AC_DEFINE_UNQUOTED([ACTIVE_PNOR_MAX_ALLOWED], [$ACTIVE_PNOR_MAX_ALLOWED], [The maximum allowed active pnor versions])

AC_ARG_VAR(PNOR_MSL, [The PNOR minimum ship level])
AS_IF([test "x$PNOR_MSL" == "x"], [PNOR_MSL=""])
AC_DEFINE_UNQUOTED([PNOR_MSL], ["$PNOR_MSL"], [The PNOR minimum ship level])

AC_ARG_VAR(PNOR_VERSION_PARTITION, [The name of the PNOR version partition])
AS_IF([test "x$PNOR_VERSION_PARTITION" == "x"], [PNOR_VERSION_PARTITION="VERSION"])
AC_DEFINE_UNQUOTED([PNOR_VERSION_PARTITION], ["$PNOR_VERSION_PARTITION"],
[The name of the PNOR version partition])

AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
137 changes: 137 additions & 0 deletions msl_verify.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include "config.h"

#include "msl_verify.hpp"

#include <experimental/filesystem>
#include <fstream>
#include <phosphor-logging/log.hpp>
#include <regex>

namespace openpower
{
namespace software
{
namespace image
{

namespace fs = std::experimental::filesystem;
using namespace phosphor::logging;

int MinimumShipLevel::compare(const Version& a, const Version& b)
{
if (a.major < b.major)
{
return -1;
}
else if (a.major > b.major)
{
return 1;
}

if (a.minor < b.minor)
{
return -1;
}
else if (a.minor > b.minor)
{
return 1;
}

if (a.rev < b.rev)
{
return -1;
}
else if (a.rev > b.rev)
{
return 1;
}

return 0;
}

void MinimumShipLevel::parse(const std::string& versionStr, Version& version)
{
std::smatch match;
version = {0, 0, 0};

// Match for vX.Y.Z
std::regex regex{"v([0-9]+)\\.([0-9]+)\\.([0-9]+)", std::regex::extended};

if (!std::regex_search(versionStr, match, regex))
{
// Match for vX.Y
std::regex regexShort{"v([0-9]+)\\.([0-9]+)", std::regex::extended};
if (!std::regex_search(versionStr, match, regexShort))
{
log<level::ERR>("Unable to parse PNOR version",
entry("VERSION=%s", versionStr.c_str()));
return;
}
}
else
{
// Populate Z
version.rev = std::stoi(match[3]);
}
version.major = std::stoi(match[1]);
version.minor = std::stoi(match[2]);
}

std::string MinimumShipLevel::getFunctionalVersion()
{
if (!fs::exists(PNOR_RO_ACTIVE_PATH))
{
return {};
}

fs::path versionPath(PNOR_RO_ACTIVE_PATH);
versionPath /= PNOR_VERSION_PARTITION;
if (!fs::is_regular_file(versionPath))
{
return {};
}

std::ifstream versionFile(versionPath);
std::string versionStr;
std::getline(versionFile, versionStr);

return versionStr;
}

bool MinimumShipLevel::verify()
{
if (minShipLevel.empty())
{
return true;
}

auto actual = getFunctionalVersion();
if (actual.empty())
{
return true;
}

Version minVersion = {0, 0, 0};
parse(minShipLevel, minVersion);

Version actualVersion = {0, 0, 0};
parse(actual, actualVersion);

auto rc = compare(actualVersion, minVersion);
if (rc < 0)
{
log<level::ERR>(
"PNOR Mininum Ship Level NOT met",
entry("MIN_VERSION=%s", minShipLevel.c_str()),
entry("ACTUAL_VERSION=%s", actual.c_str()),
entry("VERSION_PURPOSE=%s",
"xyz.openbmc_project.Software.Version.VersionPurpose.Host"));
return false;
}

return true;
}

} // namespace image
} // namespace software
} // namespace openpower
77 changes: 77 additions & 0 deletions msl_verify.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <string>

namespace openpower
{
namespace software
{
namespace image
{

/** @class MinimumShipLevel
* @brief Contains minimum ship level verification functions.
*/
class MinimumShipLevel
{
public:
MinimumShipLevel() = delete;
MinimumShipLevel(const MinimumShipLevel&) = delete;
MinimumShipLevel& operator=(const MinimumShipLevel&) = delete;
MinimumShipLevel(MinimumShipLevel&&) = default;
MinimumShipLevel& operator=(MinimumShipLevel&&) = default;
~MinimumShipLevel() = default;

/** @brief Constructs MinimumShipLevel.
* @param[in] minShipLevel - Minimum Ship Level string
*/
MinimumShipLevel(const std::string& minShipLevel) :
minShipLevel(minShipLevel){};

/** @brief Verify if the current PNOR version meets the min ship level
* @return true if the verification succeeded, false otherwise
*/
bool verify();

/** @brief Version components */
struct Version
{
uint8_t major;
uint8_t minor;
uint8_t rev;
};

/** @brief Get the functional PNOR version on the system
* @details If the PNOR version file is not found, don't log an error since
* all PNOR images may had been deleted. If there is an issue with
* the VERSION partition, it should be caught by the host fw code.
* @return The populated or empty version string
*/
std::string getFunctionalVersion();

/** @brief Parse the version components into a struct
* @details Version format follows a git tag convention: vX.Y[.Z]
* Reference:
* https://github.com/open-power/op-build/blob/master/openpower/package/VERSION.readme
* @param[in] versionStr - The version string to be parsed
* @param[out] version - The version struct to be populated
*/
void parse(const std::string& versionStr, Version& version);

/** @brief Compare the versions provided
* @param[in] a - The first version to compare
* @param[in] b - The second version to compare
* @return 1 if a > b
* 0 if a = b
* -1 if a < b
*/
int compare(const Version& a, const Version& b);

private:
/** Minimum Ship Level to compare against */
std::string minShipLevel;
};

} // namespace image
} // namespace software
} // namespace openpower
16 changes: 16 additions & 0 deletions msl_verify_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "config.h"

#include "msl_verify.hpp"

int main(int argc, char* argv[])
{
using MinimumShipLevel = openpower::software::image::MinimumShipLevel;
MinimumShipLevel minimumShipLevel(PNOR_MSL);

if (!minimumShipLevel.verify())
{
// TODO Create error log
}

return 0;
}
5 changes: 4 additions & 1 deletion test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ utest_LDFLAGS = \
-lssl \
-lcrypto

utest_SOURCES = utest.cpp
utest_SOURCES = \
utest.cpp \
msl_verify.cpp
utest_LDADD = \
$(top_builddir)/openpower_update_manager-activation.o \
$(top_builddir)/openpower_update_manager-version.o \
Expand All @@ -36,4 +38,5 @@ utest_LDADD = \
$(top_builddir)/openpower_update_manager-item_updater.o \
$(top_builddir)/org/openbmc/Associations/openpower_update_manager-server.o \
$(top_builddir)/image_verify.cpp \
$(top_builddir)/msl_verify.cpp \
-lstdc++fs
78 changes: 78 additions & 0 deletions test/msl_verify.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "msl_verify.hpp"

#include <gtest/gtest.h>

namespace openpower
{
namespace software
{
namespace image
{

class MinimumShipLevelTest : public testing::Test
{
protected:
std::string minShipLevel = "v.2.2";
std::unique_ptr<MinimumShipLevel> minimumShipLevel;

virtual void SetUp()
{
minimumShipLevel = std::make_unique<MinimumShipLevel>(minShipLevel);
}
};

TEST_F(MinimumShipLevelTest, compare)
{
MinimumShipLevel::Version min;
MinimumShipLevel::Version actual;

min = {3, 5, 7};

// actual = min
actual = {3, 5, 7};
EXPECT_EQ(0, minimumShipLevel->compare(actual, min));

// actual < min
actual = {3, 5, 6};
EXPECT_EQ(-1, minimumShipLevel->compare(actual, min));
actual = {3, 4, 7};
EXPECT_EQ(-1, minimumShipLevel->compare(actual, min));
actual = {2, 5, 7};
EXPECT_EQ(-1, minimumShipLevel->compare(actual, min));

// actual > min
actual = {3, 5, 8};
EXPECT_EQ(1, minimumShipLevel->compare(actual, min));
actual = {3, 6, 7};
EXPECT_EQ(1, minimumShipLevel->compare(actual, min));
actual = {4, 5, 7};
EXPECT_EQ(1, minimumShipLevel->compare(actual, min));
}

TEST_F(MinimumShipLevelTest, parse)
{
MinimumShipLevel::Version version;
std::string versionStr;

versionStr = "nomatch-1.2.3-abc";
minimumShipLevel->parse(versionStr, version);
EXPECT_EQ(0, version.major);
EXPECT_EQ(0, version.minor);
EXPECT_EQ(0, version.rev);

versionStr = "xyzformat-v1.2.3-4.5abc";
minimumShipLevel->parse(versionStr, version);
EXPECT_EQ(1, version.major);
EXPECT_EQ(2, version.minor);
EXPECT_EQ(3, version.rev);

versionStr = "xyformat-system-v6.7-abc";
minimumShipLevel->parse(versionStr, version);
EXPECT_EQ(6, version.major);
EXPECT_EQ(7, version.minor);
EXPECT_EQ(0, version.rev);
}

} // namespace image
} // namespace software
} // namespace openpower

0 comments on commit 4772a94

Please sign in to comment.