1 #include "entity_manager/topology.hpp" 2 3 #include <ranges> 4 5 #include "gmock/gmock.h" 6 #include "gtest/gtest.h" 7 8 using ::testing::UnorderedElementsAre; 9 10 const std::string subchassisPath = 11 "/xyz/openbmc_project/inventory/system/chassis/Subchassis"; 12 const std::string superchassisPath = 13 "/xyz/openbmc_project/inventory/system/chassis/Superchassis"; 14 15 const Association subchassisAssoc = 16 std::make_tuple("contained_by", "containing", superchassisPath); 17 const Association powerAssoc = 18 std::make_tuple("powering", "powered_by", superchassisPath); 19 20 const nlohmann::json subchassisExposesItem = nlohmann::json::parse(R"( 21 { 22 "ConnectsToType": "BackplanePort", 23 "Name": "MyDownstreamPort", 24 "Type": "DownstreamPort" 25 } 26 )"); 27 28 const nlohmann::json powerExposesItem = nlohmann::json::parse(R"( 29 { 30 "ConnectsToType": "BackplanePort", 31 "Name": "MyDownstreamPort", 32 "Type": "DownstreamPort", 33 "PowerPort": true 34 } 35 )"); 36 37 const nlohmann::json superchassisExposesItem = nlohmann::json::parse(R"( 38 { 39 "Name": "MyBackplanePort", 40 "Type": "BackplanePort" 41 } 42 )"); 43 44 const nlohmann::json otherExposesItem = nlohmann::json::parse(R"( 45 { 46 "Name": "MyExposes", 47 "Type": "OtherType" 48 } 49 )"); 50 51 using BoardMap = std::map<std::string, std::string>; 52 53 TEST(Topology, Empty) 54 { 55 Topology topo; 56 BoardMap boards; 57 58 auto assocs = topo.getAssocs(std::views::keys(boards)); 59 60 EXPECT_EQ(assocs.size(), 0U); 61 } 62 63 TEST(Topology, EmptyExposes) 64 { 65 Topology topo; 66 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 67 68 topo.addBoard(subchassisPath, "Chassis", "BoardA", nlohmann::json()); 69 topo.addBoard(superchassisPath, "Chassis", "BoardB", nlohmann::json()); 70 71 auto assocs = topo.getAssocs(std::views::keys(boards)); 72 73 EXPECT_EQ(assocs.size(), 0U); 74 } 75 76 TEST(Topology, MissingConnectsTo) 77 { 78 const nlohmann::json subchassisMissingConnectsTo = nlohmann::json::parse(R"( 79 { 80 "Name": "MyDownstreamPort", 81 "Type": "DownstreamPort" 82 } 83 )"); 84 85 Topology topo; 86 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 87 88 topo.addBoard(subchassisPath, "Chassis", "BoardA", 89 subchassisMissingConnectsTo); 90 topo.addBoard(superchassisPath, "Chassis", "BoardB", 91 superchassisExposesItem); 92 93 auto assocs = topo.getAssocs(std::views::keys(boards)); 94 95 EXPECT_EQ(assocs.size(), 0U); 96 } 97 98 TEST(Topology, OtherExposes) 99 { 100 Topology topo; 101 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 102 103 topo.addBoard(subchassisPath, "Chassis", "BoardA", otherExposesItem); 104 topo.addBoard(superchassisPath, "Chassis", "BoardB", otherExposesItem); 105 106 auto assocs = topo.getAssocs(std::views::keys(boards)); 107 108 EXPECT_EQ(assocs.size(), 0U); 109 } 110 111 TEST(Topology, NoMatchSubchassis) 112 { 113 Topology topo; 114 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 115 116 topo.addBoard(subchassisPath, "Chassis", "BoardA", otherExposesItem); 117 topo.addBoard(superchassisPath, "Chassis", "BoardB", 118 superchassisExposesItem); 119 120 auto assocs = topo.getAssocs(std::views::keys(boards)); 121 122 EXPECT_EQ(assocs.size(), 0U); 123 } 124 125 TEST(Topology, NoMatchSuperchassis) 126 { 127 Topology topo; 128 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 129 130 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 131 topo.addBoard(superchassisPath, "Chassis", "BoardB", otherExposesItem); 132 133 auto assocs = topo.getAssocs(std::views::keys(boards)); 134 135 EXPECT_EQ(assocs.size(), 0U); 136 } 137 138 TEST(Topology, Basic) 139 { 140 Topology topo; 141 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 142 143 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 144 topo.addBoard(superchassisPath, "Chassis", "BoardB", 145 superchassisExposesItem); 146 147 auto assocs = topo.getAssocs(std::views::keys(boards)); 148 149 EXPECT_EQ(assocs.size(), 1U); 150 EXPECT_EQ(assocs[subchassisPath].size(), 1U); 151 EXPECT_TRUE(assocs[subchassisPath].contains(subchassisAssoc)); 152 } 153 154 TEST(Topology, BasicPower) 155 { 156 Topology topo; 157 BoardMap boards{{subchassisPath, "BoardA"}, {superchassisPath, "BoardB"}}; 158 159 topo.addBoard(subchassisPath, "Chassis", "BoardA", powerExposesItem); 160 topo.addBoard(superchassisPath, "Chassis", "BoardB", 161 superchassisExposesItem); 162 163 auto assocs = topo.getAssocs(std::views::keys(boards)); 164 165 EXPECT_EQ(assocs.size(), 1U); 166 EXPECT_EQ(assocs[subchassisPath].size(), 2U); 167 EXPECT_TRUE(assocs[subchassisPath].contains(subchassisAssoc)); 168 EXPECT_TRUE(assocs[subchassisPath].contains(powerAssoc)); 169 } 170 171 TEST(Topology, NoNewBoards) 172 { 173 Topology topo; 174 BoardMap boards; 175 176 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 177 topo.addBoard(superchassisPath, "Chassis", "BoardB", 178 superchassisExposesItem); 179 180 // Boards A and B aren't new, so no assocs are returned. 181 auto assocs = topo.getAssocs(std::views::keys(boards)); 182 183 EXPECT_EQ(assocs.size(), 0U); 184 } 185 186 TEST(Topology, 2Subchassis) 187 { 188 Topology topo; 189 BoardMap boards{{subchassisPath, "BoardA"}, 190 {subchassisPath + "2", "BoardB"}, 191 {superchassisPath, "BoardC"}}; 192 193 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 194 topo.addBoard(subchassisPath + "2", "Chassis", "BoardB", 195 subchassisExposesItem); 196 topo.addBoard(superchassisPath, "Chassis", "BoardC", 197 superchassisExposesItem); 198 199 auto assocs = topo.getAssocs(std::views::keys(boards)); 200 201 EXPECT_EQ(assocs.size(), 2U); 202 EXPECT_EQ(assocs[subchassisPath].size(), 1U); 203 EXPECT_TRUE(assocs[subchassisPath].contains(subchassisAssoc)); 204 EXPECT_EQ(assocs[subchassisPath + "2"].size(), 1U); 205 EXPECT_TRUE(assocs[subchassisPath + "2"].contains(subchassisAssoc)); 206 } 207 208 TEST(Topology, OneNewBoard) 209 { 210 Topology topo; 211 BoardMap boards{{subchassisPath, "BoardA"}}; 212 213 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 214 topo.addBoard(subchassisPath + "2", "Chassis", "BoardB", 215 subchassisExposesItem); 216 topo.addBoard(superchassisPath, "Chassis", "BoardC", 217 superchassisExposesItem); 218 219 // Only the assoc for BoardA should be returned 220 auto assocs = topo.getAssocs(std::views::keys(boards)); 221 222 EXPECT_EQ(assocs.size(), 1U); 223 EXPECT_EQ(assocs[subchassisPath].size(), 1U); 224 EXPECT_TRUE(assocs[subchassisPath].contains(subchassisAssoc)); 225 } 226 227 TEST(Topology, 2Superchassis) 228 { 229 Association subchassisAssoc2 = 230 std::make_tuple("contained_by", "containing", superchassisPath + "2"); 231 232 Topology topo; 233 BoardMap boards{{subchassisPath, "BoardA"}, 234 {superchassisPath, "BoardB"}, 235 {superchassisPath + "2", "BoardC"}}; 236 237 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 238 topo.addBoard(superchassisPath, "Chassis", "BoardB", 239 superchassisExposesItem); 240 topo.addBoard(superchassisPath + "2", "Chassis", "BoardC", 241 superchassisExposesItem); 242 243 auto assocs = topo.getAssocs(std::views::keys(boards)); 244 245 EXPECT_EQ(assocs.size(), 1U); 246 EXPECT_EQ(assocs[subchassisPath].size(), 2U); 247 248 EXPECT_THAT(assocs[subchassisPath], 249 UnorderedElementsAre(subchassisAssoc, subchassisAssoc2)); 250 } 251 252 TEST(Topology, 2SuperchassisAnd2Subchassis) 253 { 254 Association subchassisAssoc2 = 255 std::make_tuple("contained_by", "containing", superchassisPath + "2"); 256 257 Topology topo; 258 BoardMap boards{{subchassisPath, "BoardA"}, 259 {subchassisPath + "2", "BoardB"}, 260 {superchassisPath, "BoardC"}, 261 {superchassisPath + "2", "BoardD"}}; 262 263 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 264 topo.addBoard(subchassisPath + "2", "Chassis", "BoardB", 265 subchassisExposesItem); 266 topo.addBoard(superchassisPath, "Chassis", "BoardC", 267 superchassisExposesItem); 268 topo.addBoard(superchassisPath + "2", "Chassis", "BoardD", 269 superchassisExposesItem); 270 271 auto assocs = topo.getAssocs(std::views::keys(boards)); 272 273 EXPECT_EQ(assocs.size(), 2U); 274 EXPECT_EQ(assocs[subchassisPath].size(), 2U); 275 EXPECT_EQ(assocs[subchassisPath + "2"].size(), 2U); 276 277 EXPECT_THAT(assocs[subchassisPath], 278 UnorderedElementsAre(subchassisAssoc, subchassisAssoc2)); 279 EXPECT_THAT(assocs[subchassisPath + "2"], 280 UnorderedElementsAre(subchassisAssoc, subchassisAssoc2)); 281 } 282 283 TEST(Topology, Remove) 284 { 285 Topology topo; 286 BoardMap boards{{subchassisPath, "BoardA"}, 287 {subchassisPath + "2", "BoardB"}, 288 {superchassisPath, "BoardC"}}; 289 290 topo.addBoard(subchassisPath, "Chassis", "BoardA", subchassisExposesItem); 291 topo.addBoard(subchassisPath + "2", "Chassis", "BoardB", 292 subchassisExposesItem); 293 topo.addBoard(superchassisPath, "Chassis", "BoardC", 294 superchassisExposesItem); 295 296 { 297 auto assocs = topo.getAssocs(std::views::keys(boards)); 298 299 EXPECT_EQ(assocs.size(), 2U); 300 EXPECT_EQ(assocs[subchassisPath].size(), 1U); 301 EXPECT_TRUE(assocs[subchassisPath].contains(subchassisAssoc)); 302 EXPECT_EQ(assocs[subchassisPath + "2"].size(), 1U); 303 EXPECT_TRUE(assocs[subchassisPath + "2"].contains(subchassisAssoc)); 304 } 305 306 { 307 topo.remove("BoardA"); 308 auto assocs = topo.getAssocs(std::views::keys(boards)); 309 310 EXPECT_EQ(assocs.size(), 1U); 311 EXPECT_EQ(assocs[subchassisPath + "2"].size(), 1U); 312 EXPECT_TRUE(assocs[subchassisPath + "2"].contains(subchassisAssoc)); 313 } 314 315 { 316 topo.remove("BoardB"); 317 auto assocs = topo.getAssocs(std::views::keys(boards)); 318 319 EXPECT_EQ(assocs.size(), 0U); 320 } 321 } 322 323 TEST(Topology, SimilarToTyanS8030) 324 { 325 const std::string chassisPath = 326 "/xyz/openbmc_project/inventory/system/chassis/ChassisA"; 327 const std::string boardPath = 328 "/xyz/openbmc_project/inventory/system/board/BoardA"; 329 const std::string psu0Path = 330 "/xyz/openbmc_project/inventory/system/powersupply/PSU0"; 331 const std::string psu1Path = 332 "/xyz/openbmc_project/inventory/system/powersupply/PSU1"; 333 334 const nlohmann::json chassisContainExposesItem = nlohmann::json::parse(R"( 335 { 336 "Name": "GenericContainPort", 337 "PortType": "containing", 338 "Type": "Port" 339 } 340 )"); 341 const nlohmann::json containedByExposesItem = nlohmann::json::parse(R"( 342 { 343 "Name": "GenericContainPort", 344 "PortType": "contained_by", 345 "Type": "Port" 346 } 347 )"); 348 const nlohmann::json boardPowerExposesItem = nlohmann::json::parse(R"( 349 { 350 "Name": "GenericPowerPort", 351 "PortType": "powered_by", 352 "Type": "Port" 353 } 354 )"); 355 const nlohmann::json psuPowerExposesItem = nlohmann::json::parse(R"( 356 { 357 "Name": "GenericPowerPort", 358 "PortType": "powering", 359 "Type": "Port" 360 } 361 )"); 362 Topology topo; 363 BoardMap boards{ 364 {chassisPath, "ChassisA"}, 365 {boardPath, "BoardA"}, 366 {psu0Path, "PSU0"}, 367 {psu1Path, "PSU1"}, 368 }; 369 370 // configure the chassis to be containing something 371 topo.addBoard(chassisPath, "Chassis", "ChassisA", 372 chassisContainExposesItem); 373 374 // configure board to be contained by something 375 topo.addBoard(boardPath, "Board", "BoardA", containedByExposesItem); 376 377 // configure the board to be powered by something 378 topo.addBoard(boardPath, "Board", "BoardA", boardPowerExposesItem); 379 380 // configure the PSUs to be powering something 381 topo.addBoard(psu0Path, "PowerSupply", "PSU0", psuPowerExposesItem); 382 topo.addBoard(psu1Path, "PowerSupply", "PSU1", psuPowerExposesItem); 383 384 // configured PSUs to be contained by something 385 topo.addBoard(psu0Path, "PowerSupply", "PSU0", containedByExposesItem); 386 topo.addBoard(psu1Path, "PowerSupply", "PSU1", containedByExposesItem); 387 388 auto assocs = topo.getAssocs(std::views::keys(boards)); 389 390 EXPECT_TRUE(assocs.contains(boardPath)); 391 EXPECT_TRUE(assocs.contains(psu0Path)); 392 EXPECT_TRUE(assocs.contains(psu0Path)); 393 394 // expect chassis to contain board 395 EXPECT_EQ(assocs[boardPath].size(), 1); 396 EXPECT_TRUE(assocs[boardPath].contains( 397 {"contained_by", "containing", chassisPath})); 398 399 // expect powering association from each PSU to the board 400 // and expect each PSU to be contained by the chassis 401 EXPECT_EQ(assocs[psu0Path].size(), 2); 402 EXPECT_TRUE( 403 assocs[psu0Path].contains({"powering", "powered_by", boardPath})); 404 EXPECT_TRUE( 405 assocs[psu0Path].contains({"contained_by", "containing", chassisPath})); 406 407 EXPECT_EQ(assocs[psu1Path].size(), 2); 408 EXPECT_TRUE( 409 assocs[psu1Path].contains({"powering", "powered_by", boardPath})); 410 EXPECT_TRUE( 411 assocs[psu1Path].contains({"contained_by", "containing", chassisPath})); 412 } 413 414 static nlohmann::json makeExposesItem(const std::string& name, 415 const std::string& assocName) 416 { 417 nlohmann::json exposesItem = nlohmann::json::parse(R"( 418 { 419 "Name": "REPLACE", 420 "PortType": "REPLACE", 421 "Type": "Port" 422 } 423 )"); 424 425 exposesItem["Name"] = name; 426 exposesItem["PortType"] = assocName; 427 428 return exposesItem; 429 } 430 431 static nlohmann::json makeContainedPortExposesItem(const std::string& name) 432 { 433 return makeExposesItem(name, "contained_by"); 434 } 435 436 static nlohmann::json makeContainingPortExposesItem(const std::string& name) 437 { 438 return makeExposesItem(name, "containing"); 439 } 440 441 TEST(Topology, SimilarToYosemiteV3) 442 { 443 const std::string blade1Path = 444 "/xyz/openbmc_project/inventory/system/board/Blade1"; 445 const std::string blade2Path = 446 "/xyz/openbmc_project/inventory/system/board/Blade2"; 447 const std::string blade3Path = 448 "/xyz/openbmc_project/inventory/system/board/Blade3"; 449 const std::string blade4Path = 450 "/xyz/openbmc_project/inventory/system/board/Blade4"; 451 const std::string chassis1Path = 452 "/xyz/openbmc_project/inventory/system/chassis/Blade1Chassis"; 453 const std::string chassis2Path = 454 "/xyz/openbmc_project/inventory/system/chassis/Blade2Chassis"; 455 const std::string chassis3Path = 456 "/xyz/openbmc_project/inventory/system/chassis/Blade3Chassis"; 457 const std::string chassis4Path = 458 "/xyz/openbmc_project/inventory/system/chassis/Blade4Chassis"; 459 const std::string superChassisPath = 460 "/xyz/openbmc_project/inventory/system/chassis/SuperChassis"; 461 462 const nlohmann::json blade1ExposesItem = 463 makeContainedPortExposesItem("Blade1Port"); 464 const nlohmann::json blade2ExposesItem = 465 makeContainedPortExposesItem("Blade2Port"); 466 const nlohmann::json blade3ExposesItem = 467 makeContainedPortExposesItem("Blade3Port"); 468 const nlohmann::json blade4ExposesItem = 469 makeContainedPortExposesItem("Blade4Port"); 470 471 const nlohmann::json chassis1ExposesItem = 472 makeContainingPortExposesItem("Blade1Port"); 473 const nlohmann::json chassis2ExposesItem = 474 makeContainingPortExposesItem("Blade2Port"); 475 const nlohmann::json chassis3ExposesItem = 476 makeContainingPortExposesItem("Blade3Port"); 477 const nlohmann::json chassis4ExposesItem = 478 makeContainingPortExposesItem("Blade4Port"); 479 480 const nlohmann::json chassis1ExposesItem2 = 481 makeContainedPortExposesItem("SuperChassisPort"); 482 const nlohmann::json chassis2ExposesItem2 = 483 makeContainedPortExposesItem("SuperChassisPort"); 484 const nlohmann::json chassis3ExposesItem2 = 485 makeContainedPortExposesItem("SuperChassisPort"); 486 const nlohmann::json chassis4ExposesItem2 = 487 makeContainedPortExposesItem("SuperChassisPort"); 488 489 const nlohmann::json superChassisExposesItem = 490 makeContainingPortExposesItem("SuperChassisPort"); 491 492 Topology topo; 493 BoardMap boards{ 494 {blade1Path, "Blade1"}, 495 {blade2Path, "Blade2"}, 496 {blade3Path, "Blade3"}, 497 {blade4Path, "Blade4"}, 498 {chassis1Path, "Chassis1"}, 499 {chassis2Path, "Chassis2"}, 500 {chassis3Path, "Chassis3"}, 501 {chassis4Path, "Chassis4"}, 502 {superChassisPath, "SuperChassis"}, 503 }; 504 505 topo.addBoard(blade1Path, "Board", "Blade1", blade1ExposesItem); 506 topo.addBoard(blade2Path, "Board", "Blade2", blade2ExposesItem); 507 topo.addBoard(blade3Path, "Board", "Blade3", blade3ExposesItem); 508 topo.addBoard(blade4Path, "Board", "Blade4", blade4ExposesItem); 509 510 topo.addBoard(chassis1Path, "Chassis", "Chassis1", chassis1ExposesItem); 511 topo.addBoard(chassis2Path, "Chassis", "Chassis2", chassis2ExposesItem); 512 topo.addBoard(chassis3Path, "Chassis", "Chassis3", chassis3ExposesItem); 513 topo.addBoard(chassis4Path, "Chassis", "Chassis4", chassis4ExposesItem); 514 515 topo.addBoard(chassis1Path, "Chassis", "Chassis1", chassis1ExposesItem2); 516 topo.addBoard(chassis2Path, "Chassis", "Chassis2", chassis2ExposesItem2); 517 topo.addBoard(chassis3Path, "Chassis", "Chassis3", chassis3ExposesItem2); 518 topo.addBoard(chassis4Path, "Chassis", "Chassis4", chassis4ExposesItem2); 519 520 topo.addBoard(superChassisPath, "Chassis", "SuperChassis", 521 superChassisExposesItem); 522 523 auto assocs = topo.getAssocs(std::views::keys(boards)); 524 525 // all blades are contained by their respective chassis 526 EXPECT_EQ(assocs[blade1Path].size(), 1); 527 EXPECT_TRUE(assocs[blade1Path].contains( 528 {"contained_by", "containing", chassis1Path})); 529 530 EXPECT_EQ(assocs[blade2Path].size(), 1); 531 EXPECT_TRUE(assocs[blade2Path].contains( 532 {"contained_by", "containing", chassis2Path})); 533 534 EXPECT_EQ(assocs[blade3Path].size(), 1); 535 EXPECT_TRUE(assocs[blade3Path].contains( 536 {"contained_by", "containing", chassis3Path})); 537 538 EXPECT_EQ(assocs[blade4Path].size(), 1); 539 EXPECT_TRUE(assocs[blade4Path].contains( 540 {"contained_by", "containing", chassis4Path})); 541 542 // all chassis are contained by the superchassis 543 EXPECT_TRUE(assocs[chassis1Path].contains( 544 {"contained_by", "containing", superChassisPath})); 545 546 EXPECT_TRUE(assocs[chassis2Path].contains( 547 {"contained_by", "containing", superChassisPath})); 548 549 EXPECT_TRUE(assocs[chassis3Path].contains( 550 {"contained_by", "containing", superChassisPath})); 551 552 EXPECT_TRUE(assocs[chassis4Path].contains( 553 {"contained_by", "containing", superChassisPath})); 554 } 555