PEL: Still create callout without full loc code
When PELs are created early during a power on reset, the service that
expands location codes may not be online yet. Previously, callouts just
wouldn't be created if a location code couldn't be expanded.
Fix that so that a callout is still created with the unexpanded location
code, as that is still better than nothing. In those cases, the
CC/PN/FN fields would also be empty since those are obtained by finding
the FRU inventory path from the location code.
Also create a similar callout in the case where the call to get the
inventory path from a location code fails.
This applies for callouts created via:
- The PEL message registry
- The device path lookup tables
- JSON FFDC files
Tested:
New unit tests that cover these paths pass.
On a system, after stopping vpd-manager.service, will get callouts like:
```
"Callout Section": {
"Callout Count": "4",
"Callouts": [{
"FRU Type": "Normal Hardware FRU",
"Priority": "Mandatory, replace all with this type as a unit",
"Location Code": "P0-C5",
"Part Number": "",
"CCIN": "",
"Serial Number": ""
}, {
"FRU Type": "Normal Hardware FRU",
"Priority": "Mandatory, replace all with this type as a unit",
"Location Code": "E0",
"Part Number": "",
"CCIN": "",
"Serial Number": ""
}, {
"FRU Type": "Normal Hardware FRU",
"Priority": "Lowest priority replacement",
"Location Code": "P0",
"Part Number": "",
"CCIN": "",
"Serial Number": ""
}, {
"FRU Type": "Normal Hardware FRU",
"Priority": "Lowest priority replacement",
"Location Code": "P0-T4",
"Part Number": "",
"CCIN": "",
"Serial Number": ""
}]
}
```
Change-Id: Ia7e94f9d4848f78ba0b00023438c4db4135fed75
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index 2c49a7b..44c0f61 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -775,6 +775,118 @@
}
}
+TEST_F(SRCTest, RegistryCalloutCantGetLocTest)
+{
+ message::Entry entry;
+ entry.src.type = 0xBD;
+ entry.src.reasonCode = 0xABCD;
+ entry.src.deconfigFlag = true;
+ entry.src.checkstopFlag = true;
+ entry.subsystem = 0x42;
+
+ entry.callouts = R"(
+ [{
+ "CalloutList":
+ [
+ {
+ "Priority": "high",
+ "LocCode": "P0-C8"
+ },
+ {
+ "Priority": "medium",
+ "LocCode": "P0-C9"
+ }
+ ]
+ }])"_json;
+
+ {
+ // The calls to expand the location codes will fail, but it should
+ // still create the callouts with the unexpanded values and no HW
+ // fields.
+ AdditionalData ad;
+ NiceMock<MockDataInterface> dataIface;
+ std::vector<std::string> names{"systemC"};
+
+ EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
+
+ EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
+ .WillRepeatedly(Throw(std::runtime_error("Fail")));
+
+ EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
+ .WillRepeatedly(Throw(std::runtime_error("Fail")));
+
+ EXPECT_CALL(dataIface, getInventoryFromLocCode(_, _, _)).Times(0);
+
+ EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
+
+ SRC src{entry, ad, dataIface};
+
+ auto& callouts = src.callouts()->callouts();
+ ASSERT_EQ(callouts.size(), 2);
+
+ // Only unexpanded location codes
+ EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
+ EXPECT_EQ(callouts[0]->priority(), 'H');
+
+ auto& fru1 = callouts[0]->fruIdentity();
+ EXPECT_EQ(fru1->getPN().value(), "");
+ EXPECT_EQ(fru1->getCCIN().value(), "");
+ EXPECT_EQ(fru1->getSN().value(), "");
+
+ EXPECT_EQ(callouts[1]->locationCode(), "P0-C9");
+ EXPECT_EQ(callouts[1]->priority(), 'M');
+
+ auto& fru2 = callouts[1]->fruIdentity();
+ EXPECT_EQ(fru2->getPN().value(), "");
+ EXPECT_EQ(fru2->getCCIN().value(), "");
+ EXPECT_EQ(fru2->getSN().value(), "");
+ }
+}
+
+TEST_F(SRCTest, TrustedSymbolicFRUCantGetLocTest)
+{
+ message::Entry entry;
+ entry.src.type = 0xBD;
+ entry.src.reasonCode = 0xABCD;
+ entry.subsystem = 0x42;
+
+ entry.callouts = R"(
+ [{
+ "CalloutList":
+ [
+ {
+ "Priority": "medium",
+ "LocCode": "P0-C8",
+ "SymbolicFRUTrusted": "pwrsply"
+ }
+ ]
+ }])"_json;
+
+ std::map<std::string, std::string> adData;
+ AdditionalData ad{adData};
+ NiceMock<MockDataInterface> dataIface;
+ std::vector<std::string> names{"systemA"};
+
+ EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
+
+ // The call to expand the location code will fail, but it should
+ // still create the callout with the unexpanded value and the
+ // symbolic FRU can't be trusted.
+ EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
+ .WillRepeatedly(Throw(std::runtime_error("Fail")));
+
+ SRC src{entry, ad, dataIface};
+
+ auto& callouts = src.callouts()->callouts();
+ ASSERT_EQ(callouts.size(), 1);
+
+ EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
+ EXPECT_EQ(callouts[0]->priority(), 'M');
+ auto& fru = callouts[0]->fruIdentity();
+ EXPECT_EQ(fru->getPN().value(), "PWRSPLY");
+ EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
+}
+
// Test looking up device path fails in the callout jSON.
TEST_F(SRCTest, DevicePathCalloutTest)
{
@@ -975,6 +1087,96 @@
"Problem looking up I2C callouts on 22 153: "
"[json.exception.out_of_range.403] key '22' not found");
}
+}
+
+TEST_F(SRCTest, DevicePathCantGetLocTest)
+{
+ message::Entry entry;
+ entry.src.type = 0xBD;
+ entry.src.reasonCode = 0xABCD;
+ entry.subsystem = 0x42;
+
+ const auto calloutJSON = R"(
+ {
+ "I2C":
+ {
+ "14":
+ {
+ "114":
+ {
+ "Callouts":[
+ {
+ "Name": "/chassis/motherboard/cpu0",
+ "LocationCode": "P1-C40",
+ "Priority": "H"
+ },
+ {
+ "Name": "/chassis/motherboard",
+ "LocationCode": "P1",
+ "Priority": "M"
+ }
+ ],
+ "Dest": "proc 0 target"
+ }
+ }
+ }
+ })";
+
+ auto dataPath = getPELReadOnlyDataPath();
+ std::ofstream file{dataPath / "systemA_dev_callouts.json"};
+ file << calloutJSON;
+ file.close();
+
+ NiceMock<MockDataInterface> dataIface;
+ std::vector<std::string> names{"systemA"};
+
+ EXPECT_CALL(dataIface, getSystemNames).WillRepeatedly(Return(names));
+
+ // The calls to expand the location codes will fail, so still create
+ // the callouts with the unexpanded values and no HW fields
+
+ EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
+ .WillRepeatedly(Throw(std::runtime_error("Fail")));
+
+ EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
+ .WillRepeatedly(Throw(std::runtime_error("Fail")));
+
+ EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
+ .Times(0);
+ EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)).Times(0);
+
+ std::map<std::string, std::string> items{
+ {"CALLOUT_ERRNO", "5"},
+ {"CALLOUT_DEVICE_PATH",
+ "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}};
+
+ AdditionalData ad{items};
+ SRC src{entry, ad, dataIface};
+
+ ASSERT_TRUE(src.callouts());
+ auto& callouts = src.callouts()->callouts();
+
+ ASSERT_EQ(callouts.size(), 2);
+
+ // Should just contain the unexpanded location codes
+ {
+ EXPECT_EQ(callouts[0]->priority(), 'H');
+ EXPECT_EQ(callouts[0]->locationCode(), "P1-C40");
+
+ auto& fru = callouts[0]->fruIdentity();
+ EXPECT_EQ(fru->getPN().value(), "");
+ EXPECT_EQ(fru->getCCIN().value(), "");
+ EXPECT_EQ(fru->getSN().value(), "");
+ }
+ {
+ EXPECT_EQ(callouts[1]->priority(), 'M');
+ EXPECT_EQ(callouts[1]->locationCode(), "P1");
+
+ auto& fru = callouts[1]->fruIdentity();
+ EXPECT_EQ(fru->getPN().value(), "");
+ EXPECT_EQ(fru->getCCIN().value(), "");
+ EXPECT_EQ(fru->getSN().value(), "");
+ }
fs::remove_all(dataPath);
}
@@ -1249,7 +1451,8 @@
}
// Callout 1 mock calls
- // getInventoryFromLocCode will fail
+ // getInventoryFromLocCode will fail, so a callout with just the
+ // location code will be created.
{
EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
.Times(1)
@@ -1266,26 +1469,34 @@
const auto& callouts = src.callouts()->callouts();
- // Only the first callout was successful
- ASSERT_EQ(callouts.size(), 1);
+ // The first callout will have the unexpanded location code.
+ ASSERT_EQ(callouts.size(), 2);
- {
- EXPECT_EQ(callouts[0]->priority(), 'H');
- EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
+ EXPECT_EQ(callouts[0]->priority(), 'H');
+ EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
- auto& fru = callouts[0]->fruIdentity();
- EXPECT_EQ(fru->getPN().value(), "1234567");
- EXPECT_EQ(fru->getCCIN().value(), "CCCC");
- EXPECT_EQ(fru->getSN().value(), "123456789ABC");
- EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
- }
+ auto& fru0 = callouts[0]->fruIdentity();
+ EXPECT_EQ(fru0->getPN().value(), "1234567");
+ EXPECT_EQ(fru0->getCCIN().value(), "CCCC");
+ EXPECT_EQ(fru0->getSN().value(), "123456789ABC");
+ EXPECT_EQ(fru0->failingComponentType(), src::FRUIdentity::hardwareFRU);
+
+ // The second callout will have empty HW details.
+ EXPECT_EQ(callouts[1]->priority(), 'H');
+ EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C2");
+
+ auto& fru1 = callouts[1]->fruIdentity();
+ EXPECT_EQ(fru1->getPN().value(), "");
+ EXPECT_EQ(fru1->getCCIN().value(), "");
+ EXPECT_EQ(fru1->getSN().value(), "");
+ EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::hardwareFRU);
const auto& data = src.getDebugData();
ASSERT_EQ(data.size(), 4);
EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
- EXPECT_STREQ(data[1].c_str(),
- "Failed extracting callout data from JSON: Unable to "
- "get inventory path from location code: P0-C2: Fail");
+ EXPECT_STREQ(
+ data[1].c_str(),
+ "Unable to get inventory path from location code: P0-C2: Fail");
EXPECT_STREQ(data[2].c_str(),
"Failed extracting callout data from JSON: "
"[json.exception.out_of_range.403] key 'Priority' not found");