blob: 11b15d410e0194fd4060c3171884fbdf199eda82 [file] [log] [blame]
Matt Spinler97f7abc2019-11-06 09:40:23 -06001/**
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 */
Matt Spinlerf9bae182019-10-09 13:37:38 -050016#include "extensions/openpower-pels/src.hpp"
Matt Spinler075e5ba2020-02-21 15:46:00 -060017#include "mocks.hpp"
Matt Spinlerf9bae182019-10-09 13:37:38 -050018#include "pel_utils.hpp"
19
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080020#include <fstream>
21
Matt Spinlerf9bae182019-10-09 13:37:38 -050022#include <gtest/gtest.h>
23
24using namespace openpower::pels;
Matt Spinlered046852020-03-13 13:58:15 -050025using ::testing::_;
William A. Kennington IIIb41fa542021-05-29 14:45:16 -070026using ::testing::DoAll;
Matt Spinlered046852020-03-13 13:58:15 -050027using ::testing::InvokeWithoutArgs;
Matt Spinler075e5ba2020-02-21 15:46:00 -060028using ::testing::NiceMock;
29using ::testing::Return;
Matt Spinlered046852020-03-13 13:58:15 -050030using ::testing::SetArgReferee;
Matt Spinler3bdd0112020-08-27 10:24:34 -050031using ::testing::Throw;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080032namespace fs = std::filesystem;
Matt Spinlerf9bae182019-10-09 13:37:38 -050033
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080034const auto testRegistry = R"(
35{
36"PELs":
37[
38 {
39 "Name": "xyz.openbmc_project.Error.Test",
40 "Subsystem": "bmc_firmware",
41 "SRC":
42 {
43 "ReasonCode": "0xABCD",
44 "Words6To9":
45 {
46 "6":
47 {
48 "Description": "Component ID",
49 "AdditionalDataPropSource": "COMPID"
50 },
51 "7":
52 {
53 "Description": "Failure count",
54 "AdditionalDataPropSource": "FREQUENCY"
55 },
56 "8":
57 {
58 "Description": "Time period",
59 "AdditionalDataPropSource": "DURATION"
60 },
61 "9":
62 {
63 "Description": "Error code",
64 "AdditionalDataPropSource": "ERRORCODE"
65 }
66 }
67 },
68 "Documentation":
69 {
70 "Description": "A Component Fault",
71 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
72 "MessageArgSources":
73 [
74 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
75 ]
76 }
77 }
78]
79}
80)";
81
82class SRCTest : public ::testing::Test
83{
84 protected:
85 static void SetUpTestCase()
86 {
87 char path[] = "/tmp/srctestXXXXXX";
88 regDir = mkdtemp(path);
89 }
90
91 static void TearDownTestCase()
92 {
93 fs::remove_all(regDir);
94 }
95
96 static std::string writeData(const char* data)
97 {
98 fs::path path = regDir / "registry.json";
99 std::ofstream stream{path};
100 stream << data;
101 return path;
102 }
103
104 static fs::path regDir;
105};
106
107fs::path SRCTest::regDir{};
108
109TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500110{
Matt Spinler42828bd2019-10-11 10:39:30 -0500111 auto data = pelDataFactory(TestPELType::primarySRCSection);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500112
113 Stream stream{data};
114 SRC src{stream};
115
116 EXPECT_TRUE(src.valid());
117
118 EXPECT_EQ(src.header().id, 0x5053);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600119 EXPECT_EQ(src.header().size, 0x50);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500120 EXPECT_EQ(src.header().version, 0x01);
121 EXPECT_EQ(src.header().subType, 0x01);
122 EXPECT_EQ(src.header().componentID, 0x0202);
123
124 EXPECT_EQ(src.version(), 0x02);
125 EXPECT_EQ(src.flags(), 0x00);
126 EXPECT_EQ(src.hexWordCount(), 9);
127 EXPECT_EQ(src.size(), 0x48);
128
129 const auto& hexwords = src.hexwordData();
Matt Spinlerbd716f02019-10-15 10:54:11 -0500130 EXPECT_EQ(0x02020255, hexwords[0]);
131 EXPECT_EQ(0x03030310, hexwords[1]);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500132 EXPECT_EQ(0x04040404, hexwords[2]);
133 EXPECT_EQ(0x05050505, hexwords[3]);
134 EXPECT_EQ(0x06060606, hexwords[4]);
135 EXPECT_EQ(0x07070707, hexwords[5]);
136 EXPECT_EQ(0x08080808, hexwords[6]);
137 EXPECT_EQ(0x09090909, hexwords[7]);
138
139 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
140 EXPECT_FALSE(src.callouts());
141
142 // Flatten
143 std::vector<uint8_t> newData;
144 Stream newStream{newData};
145
146 src.flatten(newStream);
147 EXPECT_EQ(data, newData);
148}
149
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800150TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500151{
Matt Spinler42828bd2019-10-11 10:39:30 -0500152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500153
154 Stream stream{data};
155 SRC src{stream};
156
157 EXPECT_TRUE(src.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
Matt Spinlerf9bae182019-10-09 13:37:38 -0500159
160 // Spot check the SRC fields, but they're the same as above
161 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
162
163 // There should be 2 callouts
164 const auto& calloutsSection = src.callouts();
165 ASSERT_TRUE(calloutsSection);
166 const auto& callouts = calloutsSection->callouts();
167 EXPECT_EQ(callouts.size(), 2);
168
169 // spot check that each callout has the right substructures
170 EXPECT_TRUE(callouts.front()->fruIdentity());
171 EXPECT_FALSE(callouts.front()->pceIdentity());
172 EXPECT_FALSE(callouts.front()->mru());
173
174 EXPECT_TRUE(callouts.back()->fruIdentity());
175 EXPECT_TRUE(callouts.back()->pceIdentity());
176 EXPECT_TRUE(callouts.back()->mru());
177
178 // Flatten
179 std::vector<uint8_t> newData;
180 Stream newStream{newData};
181
182 src.flatten(newStream);
183 EXPECT_EQ(data, newData);
184}
Matt Spinlerbd716f02019-10-15 10:54:11 -0500185
186// Create an SRC from the message registry
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800187TEST_F(SRCTest, CreateTestNoCallouts)
Matt Spinlerbd716f02019-10-15 10:54:11 -0500188{
189 message::Entry entry;
190 entry.src.type = 0xBD;
191 entry.src.reasonCode = 0xABCD;
192 entry.subsystem = 0x42;
193 entry.src.powerFault = true;
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800194 entry.src.hexwordADFields = {
195 {5, {"TEST1", "DESCR1"}}, // Not a user defined word
196 {6, {"TEST1", "DESCR1"}},
197 {7, {"TEST2", "DESCR2"}},
198 {8, {"TEST3", "DESCR3"}},
199 {9, {"TEST4", "DESCR4"}}};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500200
201 // Values for the SRC words pointed to above
202 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
203 "TEST3=0XDEF", "TEST4=Z"};
204 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600205 NiceMock<MockDataInterface> dataIface;
206
207 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
208
Sumit Kumar9d43a722021-08-24 09:46:19 -0500209 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
210 "system/entry"};
211 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
212 .WillOnce(Return(std::vector<bool>{false, false, false}));
213
Matt Spinler075e5ba2020-02-21 15:46:00 -0600214 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500215
216 EXPECT_TRUE(src.valid());
217 EXPECT_TRUE(src.isPowerFaultEvent());
218 EXPECT_EQ(src.size(), baseSRCSize);
219
220 const auto& hexwords = src.hexwordData();
221
222 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
223 // array index starts at 0 use the math in the [] below to make it easier
224 // to tell what is being accessed.
225 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
226 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
227 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
228 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600229 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500230
231 // Validate more fields here as the code starts filling them in.
232
233 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
234 EXPECT_EQ(hexwords[5 - 2], 0);
235
236 // The user defined hex word fields specifed in the additional data.
237 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
238 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
239 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
240 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
241
242 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
243
244 // No callouts
245 EXPECT_FALSE(src.callouts());
246
247 // May as well spot check the flatten/unflatten
248 std::vector<uint8_t> data;
249 Stream stream{data};
250 src.flatten(stream);
251
252 stream.offset(0);
253 SRC newSRC{stream};
254
255 EXPECT_TRUE(newSRC.valid());
256 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent());
257 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
258 EXPECT_FALSE(newSRC.callouts());
259}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800260
Vijay Lobof3702bb2021-04-09 15:10:19 -0500261// Create an SRC to test POWER_THERMAL_CRITICAL_FAULT set to TRUE
262// sets the power fault bit in SRC
263TEST_F(SRCTest, PowerFaultTest)
264{
265 message::Entry entry;
266 entry.src.type = 0xBD;
267 entry.src.reasonCode = 0xABCD;
268 entry.subsystem = 0x42;
269 entry.src.powerFault = false;
270
271 // Values for the SRC words pointed to above
272 std::vector<std::string> adData{"POWER_THERMAL_CRITICAL_FAULT=TRUE",
273 "TEST2=12345678", "TEST3=0XDEF", "TEST4=Z"};
274 AdditionalData ad{adData};
275 NiceMock<MockDataInterface> dataIface;
276
Sumit Kumar9d43a722021-08-24 09:46:19 -0500277 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
278 "system/entry"};
279 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
280 .WillOnce(Return(std::vector<bool>{false, false, false}));
281
Vijay Lobof3702bb2021-04-09 15:10:19 -0500282 SRC src{entry, ad, dataIface};
283
284 EXPECT_TRUE(src.valid());
285 EXPECT_TRUE(src.isPowerFaultEvent());
286 EXPECT_EQ(src.size(), baseSRCSize);
287}
288
Matt Spinler075e5ba2020-02-21 15:46:00 -0600289// Test when the CCIN string isn't a 4 character number
290TEST_F(SRCTest, BadCCINTest)
291{
292 message::Entry entry;
293 entry.src.type = 0xBD;
294 entry.src.reasonCode = 0xABCD;
295 entry.subsystem = 0x42;
296 entry.src.powerFault = false;
297
298 std::vector<std::string> adData{};
299 AdditionalData ad{adData};
300 NiceMock<MockDataInterface> dataIface;
301
Sumit Kumar9d43a722021-08-24 09:46:19 -0500302 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
303 "system/entry"};
304 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
305 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
306
Matt Spinler075e5ba2020-02-21 15:46:00 -0600307 // First it isn't a number, then it is too long,
308 // then it is empty.
309 EXPECT_CALL(dataIface, getMotherboardCCIN)
310 .WillOnce(Return("X"))
311 .WillOnce(Return("12345"))
312 .WillOnce(Return(""));
313
314 // The CCIN in the first half should still be 0 each time.
315 {
316 SRC src{entry, ad, dataIface};
317 EXPECT_TRUE(src.valid());
318 const auto& hexwords = src.hexwordData();
319 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
320 }
321
322 {
323 SRC src{entry, ad, dataIface};
324 EXPECT_TRUE(src.valid());
325 const auto& hexwords = src.hexwordData();
326 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
327 }
328
329 {
330 SRC src{entry, ad, dataIface};
331 EXPECT_TRUE(src.valid());
332 const auto& hexwords = src.hexwordData();
333 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
334 }
335}
336
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800337// Test the getErrorDetails function
338TEST_F(SRCTest, MessageSubstitutionTest)
339{
340 auto path = SRCTest::writeData(testRegistry);
341 message::Registry registry{path};
342 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
343
344 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
345 "DURATION=30", "ERRORCODE=0x01ABCDEF"};
346 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600347 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800348
Sumit Kumar9d43a722021-08-24 09:46:19 -0500349 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
350 "system/entry"};
351 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
352 .WillOnce(Return(std::vector<bool>{false, false, false}));
353
Matt Spinler075e5ba2020-02-21 15:46:00 -0600354 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800355 EXPECT_TRUE(src.valid());
356
357 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
358 ASSERT_TRUE(errorDetails);
Zane Shelley39936e32021-11-13 16:19:34 -0600359 EXPECT_EQ(errorDetails.value(),
360 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
361 "with ErrorCode 0x01ABCDEF");
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800362}
Matt Spinlered046852020-03-13 13:58:15 -0500363// Test that an inventory path callout string is
364// converted into the appropriate FRU callout.
365TEST_F(SRCTest, InventoryCalloutTest)
366{
367 message::Entry entry;
368 entry.src.type = 0xBD;
369 entry.src.reasonCode = 0xABCD;
370 entry.subsystem = 0x42;
371 entry.src.powerFault = false;
372
373 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
374 AdditionalData ad{adData};
375 NiceMock<MockDataInterface> dataIface;
376
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500377 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
378 .WillOnce(Return("UTMS-P1"));
379
380 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500381 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500382 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
383 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500384
Sumit Kumar9d43a722021-08-24 09:46:19 -0500385 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
386 "system/entry"};
387 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
388 .WillOnce(Return(std::vector<bool>{false, false, false}));
389
Matt Spinlered046852020-03-13 13:58:15 -0500390 SRC src{entry, ad, dataIface};
391 EXPECT_TRUE(src.valid());
392
393 ASSERT_TRUE(src.callouts());
394
395 EXPECT_EQ(src.callouts()->callouts().size(), 1);
396
397 auto& callout = src.callouts()->callouts().front();
398
399 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500400 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500401
402 auto& fru = callout->fruIdentity();
403
404 EXPECT_EQ(fru->getPN().value(), "1234567");
405 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
406 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
407
408 // flatten and unflatten
409 std::vector<uint8_t> data;
410 Stream stream{data};
411 src.flatten(stream);
412
413 stream.offset(0);
414 SRC newSRC{stream};
415 EXPECT_TRUE(newSRC.valid());
416 ASSERT_TRUE(src.callouts());
417 EXPECT_EQ(src.callouts()->callouts().size(), 1);
418}
419
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500420// Test that when the location code can't be obtained that
Matt Spinler479b6922021-08-17 16:34:59 -0500421// no callout is added.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500422TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
423{
424 message::Entry entry;
425 entry.src.type = 0xBD;
426 entry.src.reasonCode = 0xABCD;
427 entry.subsystem = 0x42;
428 entry.src.powerFault = false;
429
430 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
431 AdditionalData ad{adData};
432 NiceMock<MockDataInterface> dataIface;
433
434 auto func = []() {
435 throw sdbusplus::exception::SdBusError(5, "Error");
436 return std::string{};
437 };
438
439 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
440 .Times(1)
441 .WillOnce(InvokeWithoutArgs(func));
442
Sumit Kumar9d43a722021-08-24 09:46:19 -0500443 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
444 "system/entry"};
445 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
446 .WillOnce(Return(std::vector<bool>{false, false, false}));
447
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500448 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
449
450 SRC src{entry, ad, dataIface};
451 EXPECT_TRUE(src.valid());
452
Matt Spinler479b6922021-08-17 16:34:59 -0500453 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500454
455 // flatten and unflatten
456 std::vector<uint8_t> data;
457 Stream stream{data};
458 src.flatten(stream);
459
460 stream.offset(0);
461 SRC newSRC{stream};
462 EXPECT_TRUE(newSRC.valid());
Matt Spinler479b6922021-08-17 16:34:59 -0500463 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500464}
465
466// Test that when the VPD can't be obtained that
467// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500468TEST_F(SRCTest, InventoryCalloutNoVPDTest)
469{
470 message::Entry entry;
471 entry.src.type = 0xBD;
472 entry.src.reasonCode = 0xABCD;
473 entry.subsystem = 0x42;
474 entry.src.powerFault = false;
475
476 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
477 AdditionalData ad{adData};
478 NiceMock<MockDataInterface> dataIface;
479
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500480 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
481 .Times(1)
482 .WillOnce(Return("UTMS-P10"));
483
Matt Spinlered046852020-03-13 13:58:15 -0500484 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
485
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500486 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500487 .Times(1)
488 .WillOnce(InvokeWithoutArgs(func));
489
Sumit Kumar9d43a722021-08-24 09:46:19 -0500490 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
491 "system/entry"};
492 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
493 .WillOnce(Return(std::vector<bool>{false, false, false}));
494
Matt Spinlered046852020-03-13 13:58:15 -0500495 SRC src{entry, ad, dataIface};
496 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500497 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500498 EXPECT_EQ(src.callouts()->callouts().size(), 1);
499
500 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500501 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500502 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500503
504 auto& fru = callout->fruIdentity();
505
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500506 EXPECT_EQ(fru->getPN(), "");
507 EXPECT_EQ(fru->getCCIN(), "");
508 EXPECT_EQ(fru->getSN(), "");
509 EXPECT_FALSE(fru->getMaintProc());
510
Matt Spinlered046852020-03-13 13:58:15 -0500511 // flatten and unflatten
512 std::vector<uint8_t> data;
513 Stream stream{data};
514 src.flatten(stream);
515
516 stream.offset(0);
517 SRC newSRC{stream};
518 EXPECT_TRUE(newSRC.valid());
519 ASSERT_TRUE(src.callouts());
520 EXPECT_EQ(src.callouts()->callouts().size(), 1);
521}
Matt Spinler03984582020-04-09 13:17:58 -0500522
523TEST_F(SRCTest, RegistryCalloutTest)
524{
525 message::Entry entry;
526 entry.src.type = 0xBD;
527 entry.src.reasonCode = 0xABCD;
528 entry.subsystem = 0x42;
529 entry.src.powerFault = false;
530
531 entry.callouts = R"(
532 [
533 {
534 "System": "systemA",
535 "CalloutList":
536 [
537 {
538 "Priority": "high",
539 "SymbolicFRU": "service_docs"
540 },
541 {
542 "Priority": "medium",
Matt Spinler479b6922021-08-17 16:34:59 -0500543 "Procedure": "bmc_code"
Matt Spinler03984582020-04-09 13:17:58 -0500544 }
545 ]
546 },
547 {
548 "System": "systemB",
549 "CalloutList":
550 [
551 {
552 "Priority": "high",
553 "LocCode": "P0-C8",
554 "SymbolicFRUTrusted": "service_docs"
555 },
556 {
557 "Priority": "medium",
558 "SymbolicFRUTrusted": "service_docs"
559 }
560 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500561 },
562 {
563 "System": "systemC",
564 "CalloutList":
565 [
566 {
567 "Priority": "high",
568 "LocCode": "P0-C8"
569 },
570 {
571 "Priority": "medium",
572 "LocCode": "P0-C9"
573 }
574 ]
Matt Spinler03984582020-04-09 13:17:58 -0500575 }
576 ])"_json;
577
578 {
579 // Call out a symbolic FRU and a procedure
580 AdditionalData ad;
581 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500582 std::vector<std::string> names{"systemA"};
583
Matt Spinler1ab66962020-10-29 13:21:44 -0500584 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500585
Sumit Kumar9d43a722021-08-24 09:46:19 -0500586 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
587 "system/entry"};
588 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
589 .WillOnce(Return(std::vector<bool>{false, false, false}));
590
Matt Spinler03984582020-04-09 13:17:58 -0500591 SRC src{entry, ad, dataIface};
592
593 auto& callouts = src.callouts()->callouts();
594 ASSERT_EQ(callouts.size(), 2);
595
596 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
597 EXPECT_EQ(callouts[0]->priority(), 'H');
598
599 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
600 EXPECT_EQ(callouts[1]->priority(), 'M');
601
602 auto& fru1 = callouts[0]->fruIdentity();
603 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
604 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
605 EXPECT_FALSE(fru1->getMaintProc());
606 EXPECT_FALSE(fru1->getSN());
607 EXPECT_FALSE(fru1->getCCIN());
608
609 auto& fru2 = callouts[1]->fruIdentity();
Matt Spinlerea2873d2021-08-18 10:35:40 -0500610 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
Matt Spinler03984582020-04-09 13:17:58 -0500611 EXPECT_EQ(fru2->failingComponentType(),
612 src::FRUIdentity::maintenanceProc);
613 EXPECT_FALSE(fru2->getPN());
614 EXPECT_FALSE(fru2->getSN());
615 EXPECT_FALSE(fru2->getCCIN());
616 }
617
618 {
619 // Call out a trusted symbolic FRU with a location code, and
620 // another one without.
621 AdditionalData ad;
622 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500623 std::vector<std::string> names{"systemB"};
624
Matt Spinleraf191c72020-06-04 11:35:13 -0500625 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500626 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500627
Sumit Kumar9d43a722021-08-24 09:46:19 -0500628 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
629 "system/entry"};
630 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
631 .WillOnce(Return(std::vector<bool>{false, false, false}));
632
Matt Spinler03984582020-04-09 13:17:58 -0500633 SRC src{entry, ad, dataIface};
634
635 auto& callouts = src.callouts()->callouts();
636 EXPECT_EQ(callouts.size(), 2);
637
638 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
639 EXPECT_EQ(callouts[0]->priority(), 'H');
640
641 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
642 EXPECT_EQ(callouts[1]->priority(), 'M');
643
644 auto& fru1 = callouts[0]->fruIdentity();
645 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
646 EXPECT_EQ(fru1->failingComponentType(),
647 src::FRUIdentity::symbolicFRUTrustedLocCode);
648 EXPECT_FALSE(fru1->getMaintProc());
649 EXPECT_FALSE(fru1->getSN());
650 EXPECT_FALSE(fru1->getCCIN());
651
652 // It asked for a trusted symbolic FRU, but no location code
653 // was provided so it is switched back to a normal one
654 auto& fru2 = callouts[1]->fruIdentity();
655 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
656 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
657 EXPECT_FALSE(fru2->getMaintProc());
658 EXPECT_FALSE(fru2->getSN());
659 EXPECT_FALSE(fru2->getCCIN());
660 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500661
662 {
663 // Two hardware callouts
664 AdditionalData ad;
665 NiceMock<MockDataInterface> dataIface;
666 std::vector<std::string> names{"systemC"};
667
Sumit Kumar9d43a722021-08-24 09:46:19 -0500668 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
669 "system/entry"};
670 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
671 .WillOnce(Return(std::vector<bool>{false, false, false}));
672
Matt Spinler1ab66962020-10-29 13:21:44 -0500673 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500674
675 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
676 .WillOnce(Return("UXXX-P0-C8"));
677
678 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
679 .WillOnce(Return("UXXX-P0-C9"));
680
Matt Spinler2f9225a2020-08-05 12:58:49 -0500681 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500682 .WillOnce(Return(
683 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
684
Matt Spinler2f9225a2020-08-05 12:58:49 -0500685 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500686 .WillOnce(Return(
687 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
688
689 EXPECT_CALL(
690 dataIface,
691 getHWCalloutFields(
692 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
693 _))
694 .Times(1)
695 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
696 SetArgReferee<2>("CCCC"),
697 SetArgReferee<3>("123456789ABC")));
698
699 EXPECT_CALL(
700 dataIface,
701 getHWCalloutFields(
702 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
703 _))
704 .Times(1)
705 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
706 SetArgReferee<2>("DDDD"),
707 SetArgReferee<3>("23456789ABCD")));
708
709 SRC src{entry, ad, dataIface};
710
711 auto& callouts = src.callouts()->callouts();
712 EXPECT_EQ(callouts.size(), 2);
713
714 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
715 EXPECT_EQ(callouts[0]->priority(), 'H');
716
717 auto& fru1 = callouts[0]->fruIdentity();
718 EXPECT_EQ(fru1->getPN().value(), "1234567");
719 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
720 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
721
722 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
723 EXPECT_EQ(callouts[1]->priority(), 'M');
724
725 auto& fru2 = callouts[1]->fruIdentity();
726 EXPECT_EQ(fru2->getPN().value(), "2345678");
727 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
728 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
729 }
Matt Spinler03984582020-04-09 13:17:58 -0500730}
Matt Spinler717de422020-06-04 13:10:14 -0500731
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500732// Test that a symbolic FRU with a trusted location code callout
733// from the registry can get its location from the
734// CALLOUT_INVENTORY_PATH AdditionalData entry.
735TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
736{
737 message::Entry entry;
738 entry.src.type = 0xBD;
739 entry.src.reasonCode = 0xABCD;
740 entry.subsystem = 0x42;
741 entry.src.powerFault = false;
742
743 entry.callouts = R"(
744 [{
745 "CalloutList":
746 [
747 {
748 "Priority": "high",
749 "SymbolicFRUTrusted": "service_docs",
750 "UseInventoryLocCode": true
751 },
752 {
753 "Priority": "medium",
754 "LocCode": "P0-C8",
755 "SymbolicFRUTrusted": "pwrsply"
756 }
757 ]
758 }])"_json;
759
760 {
761 // The location code for the first symbolic FRU callout will
762 // come from this inventory path since UseInventoryLocCode is set.
763 // In this case there will be no normal FRU callout for the motherboard.
764 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
765 AdditionalData ad{adData};
766 NiceMock<MockDataInterface> dataIface;
767 std::vector<std::string> names{"systemA"};
768
Sumit Kumar9d43a722021-08-24 09:46:19 -0500769 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
770 "system/entry"};
771 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
772 .WillOnce(Return(std::vector<bool>{false, false, false}));
773
Matt Spinler1ab66962020-10-29 13:21:44 -0500774 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500775
776 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
777 .Times(1)
778 .WillOnce(Return("Ufcs-P10"));
779
780 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
781 .WillOnce(Return("Ufcs-P0-C8"));
782
783 SRC src{entry, ad, dataIface};
784
785 auto& callouts = src.callouts()->callouts();
786 EXPECT_EQ(callouts.size(), 2);
787
788 // The location code for the first symbolic FRU callout with a
789 // trusted location code comes from the motherboard.
790 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
791 EXPECT_EQ(callouts[0]->priority(), 'H');
792 auto& fru1 = callouts[0]->fruIdentity();
793 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
794 EXPECT_EQ(fru1->failingComponentType(),
795 src::FRUIdentity::symbolicFRUTrustedLocCode);
796
797 // The second trusted symbolic FRU callouts uses the location
798 // code in the registry as usual.
799 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
800 EXPECT_EQ(callouts[1]->priority(), 'M');
801 auto& fru2 = callouts[1]->fruIdentity();
802 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
803 EXPECT_EQ(fru2->failingComponentType(),
804 src::FRUIdentity::symbolicFRUTrustedLocCode);
805 }
806
807 {
808 // This time say we want to use the location code from
809 // the inventory, but don't pass it in and the callout should
810 // end up a regular symbolic FRU
811 entry.callouts = R"(
812 [{
813 "CalloutList":
814 [
815 {
816 "Priority": "high",
817 "SymbolicFRUTrusted": "service_docs",
818 "UseInventoryLocCode": true
819 }
820 ]
821 }])"_json;
822
823 AdditionalData ad;
824 NiceMock<MockDataInterface> dataIface;
825 std::vector<std::string> names{"systemA"};
826
Sumit Kumar9d43a722021-08-24 09:46:19 -0500827 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
828 "system/entry"};
829 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
830 .WillOnce(Return(std::vector<bool>{false, false, false}));
831
Matt Spinler1ab66962020-10-29 13:21:44 -0500832 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500833
834 SRC src{entry, ad, dataIface};
835
836 auto& callouts = src.callouts()->callouts();
837 EXPECT_EQ(callouts.size(), 1);
838
839 EXPECT_EQ(callouts[0]->locationCode(), "");
840 EXPECT_EQ(callouts[0]->priority(), 'H');
841 auto& fru1 = callouts[0]->fruIdentity();
842 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
843 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
844 }
845}
846
Matt Spinler717de422020-06-04 13:10:14 -0500847// Test looking up device path fails in the callout jSON.
848TEST_F(SRCTest, DevicePathCalloutTest)
849{
850 message::Entry entry;
851 entry.src.type = 0xBD;
852 entry.src.reasonCode = 0xABCD;
853 entry.subsystem = 0x42;
854 entry.src.powerFault = false;
855
856 const auto calloutJSON = R"(
857 {
858 "I2C":
859 {
860 "14":
861 {
862 "114":
863 {
864 "Callouts":[
865 {
866 "Name": "/chassis/motherboard/cpu0",
867 "LocationCode": "P1-C40",
868 "Priority": "H"
869 },
870 {
871 "Name": "/chassis/motherboard",
872 "LocationCode": "P1",
873 "Priority": "M"
874 },
875 {
876 "Name": "/chassis/motherboard/bmc",
877 "LocationCode": "P1-C15",
878 "Priority": "L"
879 }
880 ],
881 "Dest": "proc 0 target"
882 }
883 }
884 }
885 })";
886
887 auto dataPath = getPELReadOnlyDataPath();
888 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
889 file << calloutJSON;
890 file.close();
891
892 NiceMock<MockDataInterface> dataIface;
893 std::vector<std::string> names{"systemA"};
894
Sumit Kumar9d43a722021-08-24 09:46:19 -0500895 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
896 "system/entry"};
897 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
898 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
899
Matt Spinler717de422020-06-04 13:10:14 -0500900 EXPECT_CALL(dataIface, getSystemNames)
901 .Times(5)
Matt Spinler1ab66962020-10-29 13:21:44 -0500902 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500903
Matt Spinler2f9225a2020-08-05 12:58:49 -0500904 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500905 .Times(3)
906 .WillRepeatedly(
907 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
908
Matt Spinler2f9225a2020-08-05 12:58:49 -0500909 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500910 .Times(3)
911 .WillRepeatedly(
912 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
913
Matt Spinler2f9225a2020-08-05 12:58:49 -0500914 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500915 .Times(3)
916 .WillRepeatedly(
917 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
918
Matt Spinler0d92b522021-06-16 13:28:17 -0600919 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500920 .Times(3)
921 .WillRepeatedly(Return("Ufcs-P1-C40"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600922
923 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500924 .Times(3)
925 .WillRepeatedly(Return("Ufcs-P1"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600926
927 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500928 .Times(3)
929 .WillRepeatedly(Return("Ufcs-P1-C15"));
930
931 EXPECT_CALL(
932 dataIface,
933 getHWCalloutFields(
934 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
935 .Times(3)
936 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
937 SetArgReferee<2>("CCCC"),
938 SetArgReferee<3>("123456789ABC")));
939 EXPECT_CALL(
940 dataIface,
941 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
942 _, _, _))
943 .Times(3)
944 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
945 SetArgReferee<2>("MMMM"),
946 SetArgReferee<3>("CBA987654321")));
947 EXPECT_CALL(
948 dataIface,
949 getHWCalloutFields(
950 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
951 .Times(3)
952 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
953 SetArgReferee<2>("BBBB"),
954 SetArgReferee<3>("C123456789AB")));
955
956 // Call this below with different AdditionalData values that
957 // result in the same callouts.
958 auto checkCallouts = [&entry, &dataIface](const auto& items) {
959 AdditionalData ad{items};
960 SRC src{entry, ad, dataIface};
961
962 ASSERT_TRUE(src.callouts());
963 auto& callouts = src.callouts()->callouts();
964
965 ASSERT_EQ(callouts.size(), 3);
966
967 {
968 EXPECT_EQ(callouts[0]->priority(), 'H');
969 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
970
971 auto& fru = callouts[0]->fruIdentity();
972 EXPECT_EQ(fru->getPN().value(), "1234567");
973 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
974 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
975 }
976 {
977 EXPECT_EQ(callouts[1]->priority(), 'M');
978 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
979
980 auto& fru = callouts[1]->fruIdentity();
981 EXPECT_EQ(fru->getPN().value(), "7654321");
982 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
983 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
984 }
985 {
986 EXPECT_EQ(callouts[2]->priority(), 'L');
987 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
988
989 auto& fru = callouts[2]->fruIdentity();
990 EXPECT_EQ(fru->getPN().value(), "7123456");
991 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
992 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
993 }
994 };
995
996 {
997 // Callouts based on the device path
998 std::vector<std::string> items{
999 "CALLOUT_ERRNO=5",
1000 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
1001 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
1002
1003 checkCallouts(items);
1004 }
1005
1006 {
1007 // Callouts based on the I2C bus and address
1008 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
1009 "CALLOUT_IIC_ADDR=0x72"};
1010 checkCallouts(items);
1011 }
1012
1013 {
1014 // Also based on I2C bus and address, but with bus = /dev/i2c-14
1015 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
1016 "CALLOUT_IIC_ADDR=0x72"};
1017 checkCallouts(items);
1018 }
1019
1020 {
1021 // Callout not found
1022 std::vector<std::string> items{
1023 "CALLOUT_ERRNO=5",
1024 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
1025 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
1026
1027 AdditionalData ad{items};
1028 SRC src{entry, ad, dataIface};
1029
1030 EXPECT_FALSE(src.callouts());
1031 ASSERT_EQ(src.getDebugData().size(), 1);
1032 EXPECT_EQ(src.getDebugData()[0],
1033 "Problem looking up I2C callouts on 24 18: "
1034 "[json.exception.out_of_range.403] key '24' not found");
1035 }
1036
1037 {
1038 // Callout not found
1039 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
1040 "CALLOUT_IIC_ADDR=0x99"};
1041 AdditionalData ad{items};
1042 SRC src{entry, ad, dataIface};
1043
1044 EXPECT_FALSE(src.callouts());
1045 ASSERT_EQ(src.getDebugData().size(), 1);
1046 EXPECT_EQ(src.getDebugData()[0],
1047 "Problem looking up I2C callouts on 22 153: "
1048 "[json.exception.out_of_range.403] key '22' not found");
1049 }
1050
1051 fs::remove_all(dataPath);
1052}
Matt Spinler3bdd0112020-08-27 10:24:34 -05001053
1054// Test when callouts are passed in via JSON
1055TEST_F(SRCTest, JsonCalloutsTest)
1056{
1057 const auto jsonCallouts = R"(
1058 [
1059 {
1060 "LocationCode": "P0-C1",
1061 "Priority": "H",
1062 "MRUs": [
1063 {
1064 "ID": 42,
1065 "Priority": "H"
1066 },
1067 {
1068 "ID": 43,
1069 "Priority": "M"
1070 }
1071 ]
1072 },
1073 {
1074 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1075 "Priority": "M",
1076 "Guarded": true,
1077 "Deconfigured": true
1078 },
1079 {
1080 "Procedure": "PROCEDU",
1081 "Priority": "A"
1082 },
1083 {
1084 "SymbolicFRU": "TRUSTED",
1085 "Priority": "B",
1086 "TrustedLocationCode": true,
1087 "LocationCode": "P1-C23"
1088 },
1089 {
1090 "SymbolicFRU": "FRUTST1",
1091 "Priority": "C",
1092 "LocationCode": "P1-C24"
1093 },
1094 {
1095 "SymbolicFRU": "FRUTST2LONG",
1096 "Priority": "L"
1097 }
1098 ]
1099 )"_json;
1100
1101 message::Entry entry;
1102 entry.src.type = 0xBD;
1103 entry.src.reasonCode = 0xABCD;
1104 entry.subsystem = 0x42;
1105 entry.src.powerFault = false;
1106
1107 AdditionalData ad;
1108 NiceMock<MockDataInterface> dataIface;
1109
Sumit Kumar9d43a722021-08-24 09:46:19 -05001110 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1111 "system/entry"};
1112 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1113 .WillOnce(Return(std::vector<bool>{false, false, false}));
1114
Matt Spinler3bdd0112020-08-27 10:24:34 -05001115 // Callout 0 mock calls
1116 {
1117 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1118 .Times(1)
1119 .WillOnce(Return("UXXX-P0-C1"));
1120 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1121 .Times(1)
1122 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1123 EXPECT_CALL(
1124 dataIface,
1125 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1126 .Times(1)
1127 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1128 SetArgReferee<2>("CCCC"),
1129 SetArgReferee<3>("123456789ABC")));
1130 }
1131 // Callout 1 mock calls
1132 {
1133 EXPECT_CALL(dataIface,
1134 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1135 .WillOnce(Return("UYYY-P5"));
1136 EXPECT_CALL(
1137 dataIface,
1138 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1139 .Times(1)
1140 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1141 SetArgReferee<2>("DDDD"),
1142 SetArgReferee<3>("23456789ABCD")));
1143 }
1144 // Callout 3 mock calls
1145 {
1146 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1147 .Times(1)
1148 .WillOnce(Return("UXXX-P1-C23"));
1149 }
1150 // Callout 4 mock calls
1151 {
1152 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1153 .Times(1)
1154 .WillOnce(Return("UXXX-P1-C24"));
1155 }
1156
1157 SRC src{entry, ad, jsonCallouts, dataIface};
1158 ASSERT_TRUE(src.callouts());
1159
Matt Spinlerafa2c792020-08-27 11:01:39 -05001160 // Check the guarded and deconfigured flags
1161 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1162
Matt Spinler3bdd0112020-08-27 10:24:34 -05001163 const auto& callouts = src.callouts()->callouts();
1164 ASSERT_EQ(callouts.size(), 6);
1165
1166 // Check callout 0
1167 {
1168 EXPECT_EQ(callouts[0]->priority(), 'H');
1169 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1170
1171 auto& fru = callouts[0]->fruIdentity();
1172 EXPECT_EQ(fru->getPN().value(), "1234567");
1173 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1174 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1175 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001176
1177 auto& mruCallouts = callouts[0]->mru();
1178 ASSERT_TRUE(mruCallouts);
1179 auto& mrus = mruCallouts->mrus();
1180 ASSERT_EQ(mrus.size(), 2);
1181 EXPECT_EQ(mrus[0].id, 42);
1182 EXPECT_EQ(mrus[0].priority, 'H');
1183 EXPECT_EQ(mrus[1].id, 43);
1184 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001185 }
1186
1187 // Check callout 1
1188 {
1189 EXPECT_EQ(callouts[1]->priority(), 'M');
1190 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1191
1192 auto& fru = callouts[1]->fruIdentity();
1193 EXPECT_EQ(fru->getPN().value(), "2345678");
1194 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1195 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1196 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1197 }
1198
1199 // Check callout 2
1200 {
1201 EXPECT_EQ(callouts[2]->priority(), 'A');
1202 EXPECT_EQ(callouts[2]->locationCode(), "");
1203
1204 auto& fru = callouts[2]->fruIdentity();
1205 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1206 EXPECT_EQ(fru->failingComponentType(),
1207 src::FRUIdentity::maintenanceProc);
1208 }
1209
1210 // Check callout 3
1211 {
1212 EXPECT_EQ(callouts[3]->priority(), 'B');
1213 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1214
1215 auto& fru = callouts[3]->fruIdentity();
1216 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1217 EXPECT_EQ(fru->failingComponentType(),
1218 src::FRUIdentity::symbolicFRUTrustedLocCode);
1219 }
1220
1221 // Check callout 4
1222 {
1223 EXPECT_EQ(callouts[4]->priority(), 'C');
1224 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1225
1226 auto& fru = callouts[4]->fruIdentity();
1227 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1228 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1229 }
1230
1231 // Check callout 5
1232 {
1233 EXPECT_EQ(callouts[5]->priority(), 'L');
1234 EXPECT_EQ(callouts[5]->locationCode(), "");
1235
1236 auto& fru = callouts[5]->fruIdentity();
1237 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1238 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1239 }
1240
1241 // Check that it didn't find any errors
1242 const auto& data = src.getDebugData();
1243 EXPECT_TRUE(data.empty());
1244}
1245
1246TEST_F(SRCTest, JsonBadCalloutsTest)
1247{
1248 // The first call will have a Throw in a mock call.
1249 // The second will have a different Throw in a mock call.
1250 // The others have issues with the Priority field.
1251 const auto jsonCallouts = R"(
1252 [
1253 {
1254 "LocationCode": "P0-C1",
1255 "Priority": "H"
1256 },
1257 {
1258 "LocationCode": "P0-C2",
1259 "Priority": "H"
1260 },
1261 {
1262 "LocationCode": "P0-C3"
1263 },
1264 {
1265 "LocationCode": "P0-C4",
1266 "Priority": "X"
1267 }
1268 ]
1269 )"_json;
1270
1271 message::Entry entry;
1272 entry.src.type = 0xBD;
1273 entry.src.reasonCode = 0xABCD;
1274 entry.subsystem = 0x42;
1275 entry.src.powerFault = false;
1276
1277 AdditionalData ad;
1278 NiceMock<MockDataInterface> dataIface;
1279
Sumit Kumar9d43a722021-08-24 09:46:19 -05001280 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1281 "system/entry"};
1282 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1283 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1284
Matt Spinler3bdd0112020-08-27 10:24:34 -05001285 // Callout 0 mock calls
1286 // Expand location code will fail, so the unexpanded location
1287 // code should show up in the callout instead.
1288 {
1289 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1290 .WillOnce(Throw(std::runtime_error("Fail")));
1291
1292 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1293 .Times(1)
1294 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1295 EXPECT_CALL(
1296 dataIface,
1297 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1298 .Times(1)
1299 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1300 SetArgReferee<2>("CCCC"),
1301 SetArgReferee<3>("123456789ABC")));
1302 }
1303
1304 // Callout 1 mock calls
1305 // getInventoryFromLocCode will fail
1306 {
1307 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1308 .Times(1)
1309 .WillOnce(Return("UXXX-P0-C2"));
1310
1311 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1312 .Times(1)
1313 .WillOnce(Throw(std::runtime_error("Fail")));
1314 }
1315
1316 SRC src{entry, ad, jsonCallouts, dataIface};
1317
1318 ASSERT_TRUE(src.callouts());
1319
1320 const auto& callouts = src.callouts()->callouts();
1321
1322 // Only the first callout was successful
1323 ASSERT_EQ(callouts.size(), 1);
1324
1325 {
1326 EXPECT_EQ(callouts[0]->priority(), 'H');
1327 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1328
1329 auto& fru = callouts[0]->fruIdentity();
1330 EXPECT_EQ(fru->getPN().value(), "1234567");
1331 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1332 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1333 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1334 }
1335
1336 const auto& data = src.getDebugData();
1337 ASSERT_EQ(data.size(), 4);
1338 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1339 EXPECT_STREQ(data[1].c_str(),
1340 "Failed extracting callout data from JSON: Unable to "
1341 "get inventory path from location code: P0-C2: Fail");
1342 EXPECT_STREQ(data[2].c_str(),
1343 "Failed extracting callout data from JSON: "
1344 "[json.exception.out_of_range.403] key 'Priority' not found");
1345 EXPECT_STREQ(data[3].c_str(),
1346 "Failed extracting callout data from JSON: Invalid "
1347 "priority 'X' found in JSON callout");
1348}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001349
1350// Test that an inventory path callout can have
1351// a different priority than H.
1352TEST_F(SRCTest, InventoryCalloutTestPriority)
1353{
1354 message::Entry entry;
1355 entry.src.type = 0xBD;
1356 entry.src.reasonCode = 0xABCD;
1357 entry.subsystem = 0x42;
1358 entry.src.powerFault = false;
1359
1360 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1361 "CALLOUT_PRIORITY=M"};
1362 AdditionalData ad{adData};
1363 NiceMock<MockDataInterface> dataIface;
1364
Sumit Kumar9d43a722021-08-24 09:46:19 -05001365 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1366 "system/entry"};
1367 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1368 .WillOnce(Return(std::vector<bool>{false, false, false}));
1369
Miguel Gomez53ef1552020-10-14 21:16:32 +00001370 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1371 .WillOnce(Return("UTMS-P1"));
1372
1373 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1374 .Times(1)
1375 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1376 SetArgReferee<3>("123456789ABC")));
1377
1378 SRC src{entry, ad, dataIface};
1379 EXPECT_TRUE(src.valid());
1380
1381 ASSERT_TRUE(src.callouts());
1382
1383 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1384
1385 auto& callout = src.callouts()->callouts().front();
1386
1387 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1388 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001389}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001390
1391// Test for bmc & platform dump status bits
1392TEST_F(SRCTest, DumpStatusBitsCheck)
1393{
1394 message::Entry entry;
1395 entry.src.type = 0xBD;
1396 entry.src.reasonCode = 0xABCD;
1397 entry.subsystem = 0x42;
1398 entry.src.powerFault = false;
1399
1400 AdditionalData ad;
1401 NiceMock<MockDataInterface> dataIface;
1402 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1403 "system/entry"};
1404
1405 {
1406 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1407 .WillOnce(Return(std::vector<bool>{true, false, false}));
1408
1409 SRC src{entry, ad, dataIface};
1410 EXPECT_TRUE(src.valid());
1411
1412 const auto& hexwords = src.hexwordData();
1413 EXPECT_EQ(0x00080055, hexwords[0]);
1414 }
1415
1416 {
1417 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1418 .WillOnce(Return(std::vector<bool>{false, true, false}));
1419
1420 SRC src{entry, ad, dataIface};
1421 EXPECT_TRUE(src.valid());
1422
1423 const auto& hexwords = src.hexwordData();
1424 EXPECT_EQ(0x00000255, hexwords[0]);
1425 }
1426
1427 {
1428 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1429 .WillOnce(Return(std::vector<bool>{false, false, true}));
1430
1431 SRC src{entry, ad, dataIface};
1432 EXPECT_TRUE(src.valid());
1433
1434 const auto& hexwords = src.hexwordData();
1435 EXPECT_EQ(0x00000455, hexwords[0]);
1436 }
1437
1438 {
1439 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1440 .WillOnce(Return(std::vector<bool>{true, true, true}));
1441
1442 SRC src{entry, ad, dataIface};
1443 EXPECT_TRUE(src.valid());
1444
1445 const auto& hexwords = src.hexwordData();
1446 EXPECT_EQ(0x00080655, hexwords[0]);
1447 }
1448}