1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "elog_entry.hpp" 17 #include "extensions/openpower-pels/generic.hpp" 18 #include "extensions/openpower-pels/pel.hpp" 19 #include "mocks.hpp" 20 #include "pel_utils.hpp" 21 22 #include <filesystem> 23 #include <fstream> 24 25 #include <gtest/gtest.h> 26 27 namespace fs = std::filesystem; 28 using namespace openpower::pels; 29 using ::testing::Return; 30 31 class PELTest : public CleanLogID 32 { 33 }; 34 35 TEST_F(PELTest, FlattenTest) 36 { 37 auto data = pelDataFactory(TestPELType::pelSimple); 38 auto pel = std::make_unique<PEL>(data); 39 40 // Check a few fields 41 EXPECT_TRUE(pel->valid()); 42 EXPECT_EQ(pel->id(), 0x80818283); 43 EXPECT_EQ(pel->plid(), 0x50515253); 44 EXPECT_EQ(pel->userHeader().subsystem(), 0x10); 45 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0); 46 47 // Test that data in == data out 48 auto flattenedData = pel->data(); 49 EXPECT_EQ(data, flattenedData); 50 EXPECT_EQ(flattenedData.size(), pel->size()); 51 } 52 53 TEST_F(PELTest, CommitTimeTest) 54 { 55 auto data = pelDataFactory(TestPELType::pelSimple); 56 auto pel = std::make_unique<PEL>(data); 57 58 auto origTime = pel->commitTime(); 59 pel->setCommitTime(); 60 auto newTime = pel->commitTime(); 61 62 EXPECT_NE(origTime, newTime); 63 64 // Make a new PEL and check new value is still there 65 auto newData = pel->data(); 66 auto newPel = std::make_unique<PEL>(newData); 67 EXPECT_EQ(newTime, newPel->commitTime()); 68 } 69 70 TEST_F(PELTest, AssignIDTest) 71 { 72 auto data = pelDataFactory(TestPELType::pelSimple); 73 auto pel = std::make_unique<PEL>(data); 74 75 auto origID = pel->id(); 76 pel->assignID(); 77 auto newID = pel->id(); 78 79 EXPECT_NE(origID, newID); 80 81 // Make a new PEL and check new value is still there 82 auto newData = pel->data(); 83 auto newPel = std::make_unique<PEL>(newData); 84 EXPECT_EQ(newID, newPel->id()); 85 } 86 87 TEST_F(PELTest, WithLogIDTest) 88 { 89 auto data = pelDataFactory(TestPELType::pelSimple); 90 auto pel = std::make_unique<PEL>(data, 0x42); 91 92 EXPECT_TRUE(pel->valid()); 93 EXPECT_EQ(pel->obmcLogID(), 0x42); 94 } 95 96 TEST_F(PELTest, InvalidPELTest) 97 { 98 auto data = pelDataFactory(TestPELType::pelSimple); 99 100 // Too small 101 data.resize(PrivateHeader::flattenedSize()); 102 103 auto pel = std::make_unique<PEL>(data); 104 105 EXPECT_TRUE(pel->privateHeader().valid()); 106 EXPECT_FALSE(pel->userHeader().valid()); 107 EXPECT_FALSE(pel->valid()); 108 109 // Now corrupt the private header 110 data = pelDataFactory(TestPELType::pelSimple); 111 data.at(0) = 0; 112 pel = std::make_unique<PEL>(data); 113 114 EXPECT_FALSE(pel->privateHeader().valid()); 115 EXPECT_TRUE(pel->userHeader().valid()); 116 EXPECT_FALSE(pel->valid()); 117 } 118 119 TEST_F(PELTest, EmptyDataTest) 120 { 121 std::vector<uint8_t> data; 122 auto pel = std::make_unique<PEL>(data); 123 124 EXPECT_FALSE(pel->privateHeader().valid()); 125 EXPECT_FALSE(pel->userHeader().valid()); 126 EXPECT_FALSE(pel->valid()); 127 } 128 129 TEST_F(PELTest, CreateFromRegistryTest) 130 { 131 message::Entry regEntry; 132 uint64_t timestamp = 5; 133 134 regEntry.name = "test"; 135 regEntry.subsystem = 5; 136 regEntry.actionFlags = 0xC000; 137 regEntry.src.type = 0xBD; 138 regEntry.src.reasonCode = 0x1234; 139 140 std::vector<std::string> data{"KEY1=VALUE1"}; 141 AdditionalData ad{data}; 142 MockDataInterface dataIface; 143 144 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error, ad, 145 dataIface}; 146 147 EXPECT_TRUE(pel.valid()); 148 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42); 149 EXPECT_EQ(pel.userHeader().severity(), 0x40); 150 151 EXPECT_EQ(pel.primarySRC().value()->asciiString(), 152 "BD051234 "); 153 154 // Check that certain optional sections have been created 155 size_t mtmsCount = 0; 156 size_t euhCount = 0; 157 size_t udCount = 0; 158 159 for (const auto& section : pel.optionalSections()) 160 { 161 if (section->header().id == 162 static_cast<uint16_t>(SectionID::failingMTMS)) 163 { 164 mtmsCount++; 165 } 166 else if (section->header().id == 167 static_cast<uint16_t>(SectionID::extendedUserHeader)) 168 { 169 euhCount++; 170 } 171 else if (section->header().id == 172 static_cast<uint16_t>(SectionID::userData)) 173 { 174 udCount++; 175 } 176 } 177 178 EXPECT_EQ(mtmsCount, 1); 179 EXPECT_EQ(euhCount, 1); 180 EXPECT_EQ(udCount, 2); // AD section and sysInfo section 181 } 182 183 // Test that we'll create Generic optional sections for sections that 184 // there aren't explicit classes for. 185 TEST_F(PELTest, GenericSectionTest) 186 { 187 auto data = pelDataFactory(TestPELType::pelSimple); 188 189 std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX' 190 0x00, 0x18, // Size 191 0x01, 0x02, // version, subtype 192 0x03, 0x04, // comp ID 193 194 // some data 195 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 196 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A, 197 0x00}; 198 199 std::vector<uint8_t> section2{ 200 0x59, 0x59, // ID 'YY' 201 0x00, 0x20, // Size 202 0x01, 0x02, // version, subtype 203 0x03, 0x04, // comp ID 204 205 // some data 206 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F, 207 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; 208 209 // Add the new sections at the end 210 data.insert(data.end(), section1.begin(), section1.end()); 211 data.insert(data.end(), section2.begin(), section2.end()); 212 213 // Increment the section count 214 data.at(27) += 2; 215 auto origData = data; 216 217 PEL pel{data}; 218 219 const auto& sections = pel.optionalSections(); 220 221 bool foundXX = false; 222 bool foundYY = false; 223 224 // Check that we can find these 2 Generic sections 225 for (const auto& section : sections) 226 { 227 if (section->header().id == 0x5858) 228 { 229 foundXX = true; 230 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); 231 } 232 else if (section->header().id == 0x5959) 233 { 234 foundYY = true; 235 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); 236 } 237 } 238 239 EXPECT_TRUE(foundXX); 240 EXPECT_TRUE(foundYY); 241 242 // Now flatten and check 243 auto newData = pel.data(); 244 245 EXPECT_EQ(origData, newData); 246 } 247 248 // Test that an invalid section will still get a Generic object 249 TEST_F(PELTest, InvalidGenericTest) 250 { 251 auto data = pelDataFactory(TestPELType::pelSimple); 252 253 // Not a valid section 254 std::vector<uint8_t> section1{0x01, 0x02, 0x03}; 255 256 data.insert(data.end(), section1.begin(), section1.end()); 257 258 // Increment the section count 259 data.at(27) += 1; 260 261 PEL pel{data}; 262 EXPECT_FALSE(pel.valid()); 263 264 const auto& sections = pel.optionalSections(); 265 266 bool foundGeneric = false; 267 for (const auto& section : sections) 268 { 269 if (dynamic_cast<Generic*>(section.get()) != nullptr) 270 { 271 foundGeneric = true; 272 EXPECT_EQ(section->valid(), false); 273 break; 274 } 275 } 276 277 EXPECT_TRUE(foundGeneric); 278 } 279 280 // Create a UserData section out of AdditionalData 281 TEST_F(PELTest, MakeUDSectionTest) 282 { 283 std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3", 284 "ESEL=TEST"}; 285 AdditionalData additionalData{ad}; 286 287 auto ud = util::makeADUserDataSection(additionalData); 288 289 EXPECT_TRUE(ud->valid()); 290 EXPECT_EQ(ud->header().id, 0x5544); 291 EXPECT_EQ(ud->header().version, 0x01); 292 EXPECT_EQ(ud->header().subType, 0x01); 293 EXPECT_EQ(ud->header().componentID, 0x2000); 294 295 const auto& d = ud->data(); 296 297 std::string jsonString{d.begin(), d.end()}; 298 299 std::string expectedJSON = 300 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})"; 301 302 // The actual data is null padded to a 4B boundary. 303 std::vector<uint8_t> expectedData; 304 expectedData.resize(52, '\0'); 305 memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size()); 306 307 EXPECT_EQ(d, expectedData); 308 309 // Ensure we can read this as JSON 310 auto newJSON = nlohmann::json::parse(jsonString); 311 EXPECT_EQ(newJSON["KEY1"], "VALUE1"); 312 EXPECT_EQ(newJSON["KEY2"], "VALUE2"); 313 EXPECT_EQ(newJSON["KEY3"], "VALUE3"); 314 } 315 316 // Create the UserData section that contains system info 317 TEST_F(PELTest, SysInfoSectionTest) 318 { 319 MockDataInterface dataIface; 320 321 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234")); 322 EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready")); 323 EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On")); 324 EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off")); 325 326 std::string pid = "_PID=" + std::to_string(getpid()); 327 std::vector<std::string> ad{pid}; 328 AdditionalData additionalData{ad}; 329 330 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); 331 332 EXPECT_TRUE(ud->valid()); 333 EXPECT_EQ(ud->header().id, 0x5544); 334 EXPECT_EQ(ud->header().version, 0x01); 335 EXPECT_EQ(ud->header().subType, 0x01); 336 EXPECT_EQ(ud->header().componentID, 0x2000); 337 338 // Pull out the JSON data and check it. 339 const auto& d = ud->data(); 340 std::string jsonString{d.begin(), d.end()}; 341 auto json = nlohmann::json::parse(jsonString); 342 343 // Ensure the 'Process Name' entry contains 'pel_test' 344 auto name = json["Process Name"].get<std::string>(); 345 EXPECT_NE(name.find("pel_test"), std::string::npos); 346 347 auto version = json["BMC Version ID"].get<std::string>(); 348 EXPECT_EQ(version, "ABCD1234"); 349 350 auto state = json["BMCState"].get<std::string>(); 351 EXPECT_EQ(state, "Ready"); 352 353 state = json["ChassisState"].get<std::string>(); 354 EXPECT_EQ(state, "On"); 355 356 state = json["HostState"].get<std::string>(); 357 EXPECT_EQ(state, "Off"); 358 } 359 360 // Test that the sections that override 361 // virtual std::optional<std::string> Section::getJSON() const 362 // return valid JSON. 363 TEST_F(PELTest, SectionJSONTest) 364 { 365 auto data = pelDataFactory(TestPELType::pelSimple); 366 PEL pel{data}; 367 368 // Check that all JSON returned from the sections is 369 // parseable by nlohmann::json, which will throw an 370 // exception and fail the test if there is a problem. 371 372 // The getJSON() response needs to be wrapped in a { } to make 373 // actual valid JSON (PEL::toJSON() usually handles that). 374 375 auto jsonString = pel.privateHeader().getJSON(); 376 377 // PrivateHeader always prints JSON 378 ASSERT_TRUE(jsonString); 379 *jsonString = '{' + *jsonString + '}'; 380 auto json = nlohmann::json::parse(*jsonString); 381 382 jsonString = pel.userHeader().getJSON(); 383 384 // UserHeader always prints JSON 385 ASSERT_TRUE(jsonString); 386 *jsonString = '{' + *jsonString + '}'; 387 json = nlohmann::json::parse(*jsonString); 388 389 for (const auto& section : pel.optionalSections()) 390 { 391 // The optional sections may or may not have implemented getJSON(). 392 jsonString = section->getJSON(); 393 if (jsonString) 394 { 395 *jsonString = '{' + *jsonString + '}'; 396 auto json = nlohmann::json::parse(*jsonString); 397 } 398 } 399 } 400