| #include "app.hpp" |
| #include "async_resp.hpp" |
| #include "http_request.hpp" |
| #include "http_response.hpp" |
| #include "snapshot_fixture.hpp" |
| #include "storage.hpp" |
| #include "test/g3/mock_managed_store.hpp" |
| |
| #include <boost/beast/core/string_type.hpp> |
| #include <boost/beast/http/message.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/test/sdbus_mock.hpp> |
| |
| #include <system_error> |
| #include <unordered_set> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace redfish |
| { |
| namespace |
| { |
| |
| using ::managedStore::KeyType; |
| using ::managedStore::ManagedType; |
| using ::managedStore:: |
| SimulateFailedAsyncPostDbusCallThreadSafeWithEmptyValueAction; |
| using ::managedStore:: |
| SimulateFailedAsyncPostDbusCallThreadSafeWithMsgAndEmptyValueAction; |
| using ::managedStore::SimulateFailedAsyncSetPropertyDbusCallAction; |
| using ::managedStore::SimulateSuccessfulAsyncSetPropertyDbusCallAction; |
| using ::managedStore::ValueType; |
| using ::testing::_; |
| using ::testing::An; |
| using ::testing::Contains; |
| |
| using ::testing::NotNull; |
| |
| class StorgeSnapshotFixture : public SnapshotFixture |
| { |
| protected: |
| dbus::utility::MapperGetSubTreeResponse subtree = { |
| {"/drive_0", {{"connection", {}}}}, |
| {"/drive_1", {}}, |
| }; |
| |
| std::string path = "/xyz/openbmc_project/inventory/Board/device_1"; |
| std::string connection = "xyz.test"; |
| }; |
| |
| TEST_F(StorgeSnapshotFixture, SetResetTypeOk) |
| { |
| EXPECT_CALL(*managedStore::GetManagedObjectStore(), |
| setProperty(_, _, "xyz.openbmc_project.State.Drive", |
| "RequestedDriveTransition", |
| std::string("PowerCycle"), _)) |
| .Times(1) |
| .WillOnce(SimulateSuccessfulAsyncSetPropertyDbusCallAction:: |
| SimulateSuccessfulAsyncSetPropertyDbusCall()); |
| |
| setResetType(share_async_resp_, "drive_0", "PowerCycle", subtree); |
| |
| RunIoUntilDone(); |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, SetResetTypeFailed) |
| { |
| EXPECT_CALL(*managedStore::GetManagedObjectStore(), |
| setProperty(_, _, "xyz.openbmc_project.State.Drive", |
| "RequestedDriveTransition", |
| std::string("PowerCycle"), _)) |
| .Times(1) |
| .WillOnce(SimulateFailedAsyncSetPropertyDbusCallAction:: |
| SimulateFailedAsyncSetPropertyDbusCall()); |
| |
| setResetType(share_async_resp_, "drive_0", "PowerCycle", subtree); |
| |
| RunIoUntilDone(); |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::internal_server_error); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, SetResetTypeInvalidDriveId) |
| { |
| setResetType(share_async_resp_, "drive_2", "PowerCycle", subtree); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::not_found); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, SetResetTypeInvalidConnection) |
| { |
| setResetType(share_async_resp_, "drive_1", "PowerCycle", subtree); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::internal_server_error); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, DriveServiceLabelOnlyResponse) |
| { |
| auto response = std::make_shared<bmcweb::AsyncResp>(); |
| nlohmann::json& json = response->res.jsonValue; |
| |
| /* Mock the Drive object*/ |
| std::vector<std::string> interfaces = { |
| storage_utils::driveInterface, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| }; |
| |
| // Insert interfaces getObject call |
| KeyType locationTypeKey(ManagedType::kManagedMapperObject, path, {}); |
| dbus::utility::MapperGetObject locationTypeValue{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType) |
| .ok()); |
| |
| // Insert service label |
| std::string serviceLabel = "X2"; |
| KeyType key(ManagedType::kManagedProperty, connection, |
| sdbusplus::message::object_path(path), |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode"); |
| std::shared_ptr<ValueType> mockServiceLabel = |
| managedStore::MockManagedStoreTest::CreateValueType(serviceLabel); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, mockServiceLabel) |
| .ok()); |
| |
| /* Mock an empty Drive assocation */ |
| KeyType getAssociatedSubTreeKey( |
| ManagedType::kManagedAssociatedSubtree, path + "/chassis", |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Board"}); |
| dbus::utility::MapperGetSubTreeResponse subtree = {}; |
| std::shared_ptr<ValueType> mockSubtree = |
| managedStore::MockManagedStoreTest::CreateValueType(std::move(subtree)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(getAssociatedSubTreeKey, |
| mockSubtree) |
| .ok()); |
| |
| addAllDriveInfo(response, "", connection, path, interfaces, ""); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["ServiceLabel"], |
| serviceLabel); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["LocationType"], |
| nullptr); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| EXPECT_EQ(json["PhysicalLocation"]["Oem"]["Google"]["Devpath"], "/phys"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, DriveServiceLabelAndTypeResponse) |
| { |
| auto response = std::make_shared<bmcweb::AsyncResp>(); |
| nlohmann::json& json = response->res.jsonValue; |
| |
| /* Mock the Drive object*/ |
| std::vector<std::string> interfaces = { |
| storage_utils::driveInterface, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "xyz.openbmc_project.Inventory.Connector.Embedded", |
| }; |
| |
| // Insert interfaces getObject call |
| KeyType locationTypeKey(ManagedType::kManagedMapperObject, path, {}); |
| dbus::utility::MapperGetObject locationTypeValue{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType) |
| .ok()); |
| |
| std::string serviceLabel = "X2"; |
| KeyType key(ManagedType::kManagedProperty, connection, |
| sdbusplus::message::object_path(path), |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode"); |
| std::shared_ptr<ValueType> mockServiceLabel = |
| managedStore::MockManagedStoreTest::CreateValueType(serviceLabel); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, mockServiceLabel) |
| .ok()); |
| |
| /* Mock an empty Drive assocation */ |
| KeyType getAssociatedSubTreeKey( |
| ManagedType::kManagedAssociatedSubtree, path + "/chassis", |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Board"}); |
| dbus::utility::MapperGetSubTreeResponse subtree = {}; |
| std::shared_ptr<ValueType> mockSubtree = |
| managedStore::MockManagedStoreTest::CreateValueType(std::move(subtree)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(getAssociatedSubTreeKey, |
| mockSubtree) |
| .ok()); |
| |
| addAllDriveInfo(response, "", connection, path, interfaces, ""); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["ServiceLabel"], "X2"); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["LocationType"], |
| "Embedded"); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| EXPECT_EQ(json["PhysicalLocation"]["Oem"]["Google"]["Devpath"], |
| "/phys:device:X2"); |
| } |
| |
| /** |
| * @brief: Mock a parent object that containing the target object |
| * @param[in] objectPath the current object path |
| * @param[in] objectType the current object type presented as dbus |
| interface |
| * @param[in] parentConnection service name for the parent object |
| * @param[in] parentPath parent object path; return empty list on empty |
| string |
| * @param[in] parentType parent type present as dbus interace |
| * @param[in] serviceLabel parent service_label/location_code property; |
| return |
| * ec on dbus read when empty string |
| */ |
| void mockParent(const std::string& objectPath, const std::string& objectType, |
| const std::string& parentConnection, |
| const std::string& parentPath, const std::string& parentType, |
| const std::string& serviceLabel, |
| const std::string& locationType = "Slot") |
| { |
| // define the assocation tags |
| std::string forwardTag = "contained_by"; |
| std::string backwardTag = "containing"; |
| |
| if (objectType == storage_utils::driveInterface && |
| parentType == storage_utils::chassisInterface) |
| { |
| forwardTag = "chassis"; |
| backwardTag = "drive"; |
| } |
| else if (objectType == storage_utils::storageInterface && |
| parentType == storage_utils::driveInterface) |
| { |
| forwardTag = "drive"; |
| backwardTag = "storage"; |
| } |
| else if (objectType == storage_utils::controllerInterface && |
| parentType == storage_utils::storageInterface) |
| { |
| forwardTag = "storage"; |
| backwardTag = "storage_controller"; |
| } |
| else if (objectType == storage_utils::volumeInterface && |
| parentType == storage_utils::storageInterface) |
| { |
| forwardTag = "contained"; |
| backwardTag = "containing"; |
| } |
| |
| // insert assocation mock |
| KeyType getAssociatedSubTreeKey( |
| ManagedType::kManagedAssociatedSubtree, |
| (std::filesystem::path(objectPath) / forwardTag).string(), |
| "/xyz/openbmc_project/inventory", 0, {parentType}); |
| |
| dbus::utility::MapperGetSubTreeResponse subtree = |
| parentPath.empty() |
| ? dbus::utility::MapperGetSubTreeResponse{} |
| : dbus::utility::MapperGetSubTreeResponse{ |
| {parentPath, |
| {{parentConnection, |
| {parentType, |
| {"xyz.openbmc_project.Inventory.Decorator.LocationCode"}}}}}, |
| }; |
| std::shared_ptr<ValueType> mockSubtree = |
| managedStore::MockManagedStoreTest::CreateValueType(std::move(subtree)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(getAssociatedSubTreeKey, |
| mockSubtree) |
| .ok()); |
| |
| // insert service label mock |
| KeyType parenetLocationKey( |
| ManagedType::kManagedProperty, parentConnection, |
| sdbusplus::message::object_path(parentPath), |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode"); |
| std::shared_ptr<ValueType> mockServiceLabel = |
| serviceLabel.empty() |
| ? managedStore::MockManagedStoreTest::CreateErrorValueType( |
| serviceLabel, |
| boost::system::error_code(boost::asio::error::not_found)) |
| : managedStore::MockManagedStoreTest::CreateValueType(serviceLabel); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(parenetLocationKey, |
| mockServiceLabel) |
| .ok()); |
| |
| // insert location type mock |
| KeyType parenetLocationTypeKey(ManagedType::kManagedMapperObject, |
| parentPath, {}); |
| dbus::utility::MapperGetObject parentLocationTypeValue{ |
| {parentConnection, |
| {{"xyz.openbmc_project.Inventory.Decorator.LocationCode"}, |
| std::string{"xyz.openbmc_project.Inventory.Connector."} + |
| locationType}}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(parentLocationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(parenetLocationTypeKey, |
| mockLocationType) |
| .ok()); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, DriveLocationContextOnlyResponse) |
| { |
| auto response = std::make_shared<bmcweb::AsyncResp>(); |
| nlohmann::json& json = response->res.jsonValue; |
| |
| std::vector<std::string> interfaces = { |
| storage_utils::driveInterface, |
| }; |
| |
| // Insert interfaces getObject call |
| KeyType locationTypeKey(ManagedType::kManagedMapperObject, path, {}); |
| dbus::utility::MapperGetObject locationTypeValue{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType) |
| .ok()); |
| |
| // Parent of Drive |
| mockParent(path, storage_utils::driveInterface, "test.chassis.0", |
| "/xyz/openbmc_project/inventory/Board/chassis_0", |
| storage_utils::chassisInterface, "C"); |
| // Parent of Chasis0 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_0", |
| storage_utils::chassisInterface, "test.chassis.1", |
| "/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "A"); |
| // Parent of Chasis1 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "test.chassis.2", |
| "/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "B"); |
| |
| // Mock empty parents of Chassis2 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "", "", |
| storage_utils::chassisInterface, ""); |
| |
| addAllDriveInfo(response, "", connection, path, interfaces, ""); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["ServiceLabel"], |
| nullptr); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["LocationType"], |
| nullptr); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| EXPECT_EQ(json["PhysicalLocation"]["Oem"]["Google"]["Devpath"], |
| "/phys/B/A/C"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, DriveAllLocationResponse) |
| { |
| auto response = std::make_shared<bmcweb::AsyncResp>(); |
| nlohmann::json& json = response->res.jsonValue; |
| |
| std::vector<std::string> interfaces = { |
| storage_utils::driveInterface, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "xyz.openbmc_project.Inventory.Connector.Embedded", |
| }; |
| |
| // Insert interfaces getObject call |
| KeyType locationTypeKey(ManagedType::kManagedMapperObject, path, {}); |
| dbus::utility::MapperGetObject locationTypeValue{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType) |
| .ok()); |
| |
| std::string serviceLabel = "X2"; |
| KeyType key(ManagedType::kManagedProperty, connection, |
| sdbusplus::message::object_path(path), |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode"); |
| std::shared_ptr<ValueType> mockServiceLabel = |
| managedStore::MockManagedStoreTest::CreateValueType(serviceLabel); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, mockServiceLabel) |
| .ok()); |
| |
| // Parent of Drive |
| mockParent(path, storage_utils::driveInterface, "test.chassis.0", |
| "/xyz/openbmc_project/inventory/Board/chassis_0", |
| storage_utils::chassisInterface, "C"); |
| // Parent of Chasis0 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_0", |
| storage_utils::chassisInterface, "test.chassis.1", |
| "/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "A"); |
| // Parent of Chasis1 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "test.chassis.2", |
| "/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "B"); |
| |
| // Mock empty parents of Chassis2 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "", "", |
| storage_utils::chassisInterface, ""); |
| |
| addAllDriveInfo(response, "", connection, path, interfaces, ""); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["ServiceLabel"], "X2"); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocation"]["LocationType"], |
| "Embedded"); |
| EXPECT_EQ(json["PhysicalLocation"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| EXPECT_EQ(json["PhysicalLocation"]["Oem"]["Google"]["Devpath"], |
| "/phys/B/A/C:device:X2"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageControllerAllLocationResponse) |
| { |
| auto response = std::make_shared<bmcweb::AsyncResp>(); |
| nlohmann::json& json = response->res.jsonValue; |
| |
| /* Mock a physical controller with LocationCode/LocationType */ |
| std::vector<std::string> interfaces = { |
| storage_utils::controllerInterface, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "xyz.openbmc_project.Inventory.Connector.Embedded", |
| }; |
| |
| // Insert interfaces getObject call |
| KeyType locationTypeKey(ManagedType::kManagedMapperObject, |
| path + "/storage_controller_0", {}); |
| dbus::utility::MapperGetObject locationTypeValue{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType) |
| .ok()); |
| |
| std::string serviceLabel = "X2"; |
| KeyType key(ManagedType::kManagedProperty, connection, |
| sdbusplus::message::object_path(path) / "storage_controller_0", |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode"); |
| std::shared_ptr<ValueType> mockServiceLabel = |
| managedStore::MockManagedStoreTest::CreateValueType(serviceLabel); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, mockServiceLabel) |
| .ok()); |
| |
| // Parent of Storage Controller |
| mockParent(path + "/storage_controller_0", |
| storage_utils::controllerInterface, "test.storage.0", |
| path + "/storage_0", storage_utils::storageInterface, "Storage"); |
| |
| // Parent of Storage |
| mockParent(path + "/storage_0", storage_utils::storageInterface, |
| "test.drive.0", path + "/drive_0", storage_utils::driveInterface, |
| "Drive"); |
| |
| // Parent of Drive |
| mockParent(path + "/drive_0", storage_utils::driveInterface, |
| "test.chassis.0", path, storage_utils::chassisInterface, "C"); |
| |
| // Parent of Chasis0 |
| mockParent(path, storage_utils::chassisInterface, "test.chassis.1", |
| "/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "A"); |
| // Parent of Chasis1 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_1", |
| storage_utils::chassisInterface, "test.chassis.2", |
| "/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "B"); |
| |
| // Mock empty parents of Chassis2 |
| mockParent("/xyz/openbmc_project/inventory/Board/chassis_2", |
| storage_utils::chassisInterface, "", "", |
| storage_utils::chassisInterface, ""); |
| |
| storage_utils::tryGetLocation( |
| response, connection, path + "/storage_controller_0", |
| storage_utils::controllerInterface, interfaces); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["Location"]["PartLocation"]["ServiceLabel"], "X2"); |
| EXPECT_EQ(json["Location"]["PartLocation"]["LocationType"], "Embedded"); |
| EXPECT_EQ(json["Location"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| // storage should not be part of the location info |
| EXPECT_EQ(json["Location"]["Oem"]["Google"]["Devpath"], |
| "/phys/B/A/C/Drive:device:X2"); |
| // None-NVMe controller should have no logic tag |
| EXPECT_EQ(json["Location"]["Oem"]["Google"]["EmbeddedLocationContext"], |
| nullptr); |
| |
| json.clear(); |
| |
| /* Mock a logical NVMe controller w/o LocationType */ |
| interfaces = {storage_utils::controllerInterface, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "xyz.openbmc_project.NVMe.MetricStore"}; |
| |
| // Insert interfaces getObject call |
| dbus::utility::MapperGetObject locationTypeValue2{{connection, interfaces}}; |
| std::shared_ptr<ValueType> mockLocationType2 = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue2)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationTypeKey, |
| mockLocationType2) |
| .ok()); |
| |
| storage_utils::tryGetLocation( |
| response, connection, path + "/storage_controller_0", |
| storage_utils::controllerInterface, interfaces); |
| |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(json["Location"]["PartLocation"]["ServiceLabel"], "X2"); |
| EXPECT_EQ(json["Location"]["PartLocation"]["LocationType"], nullptr); |
| EXPECT_EQ(json["Location"]["PartLocationContext"], |
| "PlaceHolderAndShouldNotBeUsed"); |
| // storage should not be part of the devpath |
| EXPECT_EQ(json["Location"]["Oem"]["Google"]["Devpath"], |
| "/phys/B/A/C/Drive"); |
| // NVMe controller should have logic tag |
| EXPECT_EQ(json["Location"]["Oem"]["Google"]["EmbeddedLocationContext"], |
| "controller:storage_controller_0"); |
| } |
| void mockStorageObject(std::string storageName, std::string& storagePath) |
| { |
| storagePath = "/xyz/openbmc_project/inventory/system/board/ParentStorage/" + |
| storageName; |
| KeyType key(ManagedType::kManagedSubtree, "/xyz/openbmc_project/inventory", |
| 0, {"xyz.openbmc_project.Inventory.Item.Storage"}); |
| dbus::utility::MapperGetSubTreeResponse mockSubtreeResponse{ |
| {storagePath, |
| {{"xyz.openbmc_project.NVMe", |
| std::vector<std::string>{ |
| "xyz.openbmc_project.Nvme.Storage", |
| "xyz.openbmc_project.Inventory.Item.Storage"}}}}}; |
| std::shared_ptr<ValueType> subtree = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(mockSubtreeResponse)); |
| |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, subtree) |
| .ok()); |
| |
| KeyType key2(ManagedType::kManagedAssociatedSubtreePaths, |
| storagePath + "/contained_by", |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.System"}); |
| std::vector<std::string> system({}); // empty means system |
| ASSERT_TRUE( |
| managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore( |
| key2, managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(system))) |
| .ok()); |
| } |
| |
| void addVolumeToMockStorage(std::string storagePath, std::string volumeName, |
| std::string& volumePath, |
| bool includeMetricStore = true) |
| { |
| std::vector<std::string> volIfaces( |
| {"xyz.openbmc_project.Inventory.Item.Volume", |
| "xyz.openbmc_project.Nvme.Volume"}); |
| if (includeMetricStore) |
| { |
| volIfaces.push_back("xyz.openbmc_project.NVMe.MetricStore"); |
| } |
| volumePath = storagePath + "/volumes/" + volumeName; |
| KeyType getAssociatedSubTreeKey( |
| ManagedType::kManagedAssociatedSubtree, storagePath + "/containing", |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Volume"}); |
| dbus::utility::MapperGetSubTreeResponse mockSubtreeResponse{ |
| {volumePath, {{"xyz.openbmc_project.NVMe", volIfaces}}}}; |
| std::shared_ptr<ValueType> subtree = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(mockSubtreeResponse)); |
| |
| ASSERT_TRUE( |
| managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(getAssociatedSubTreeKey, subtree) |
| .ok()); |
| } |
| |
| void mockItemVolumeData(std::string volumePath) |
| { |
| KeyType key2(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NVMe", |
| volumePath, "xyz.openbmc_project.Inventory.Item.Volume"); |
| dbus::utility::DBusPropertiesMap getAllItemVolumeMap{ |
| {std::make_pair("BlockSize", 4096UL), |
| std::make_pair("Size", 3221225472000ULL)}}; |
| std::shared_ptr<ValueType> getAllItemVolume = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(getAllItemVolumeMap)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key2, getAllItemVolume) |
| .ok()); |
| } |
| |
| void mockNvmeVolumeData(std::string volumePath) |
| { |
| KeyType key3(ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NVMe", |
| volumePath, "xyz.openbmc_project.Nvme.Volume"); |
| dbus::utility::DBusPropertiesMap getAllVolumeMap{ |
| {std::make_pair("NamespaceId", 1U), std::make_pair("LBAFormat", 0UL)}}; |
| std::shared_ptr<ValueType> getAllVolume = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(getAllVolumeMap)); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key3, getAllVolume) |
| .ok()); |
| } |
| |
| void mockAvoidLocationCheckForVolume(std::string volumePath) |
| { |
| // adding empty object to avoid location check |
| KeyType locationKey(ManagedType::kManagedMapperObject, volumePath, {}); |
| dbus::utility::MapperGetObject locationTypeValue{{}}; |
| std::shared_ptr<ValueType> mockLocationType = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(locationTypeValue)); |
| ASSERT_TRUE( |
| managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(locationKey, mockLocationType) |
| .ok()); |
| |
| // create empty storage reverse association to avoid storage check |
| KeyType getAssociatedSubTreeKey( |
| ManagedType::kManagedAssociatedSubtree, volumePath + "/contained", |
| "/xyz/openbmc_project/inventory", 0, |
| {"xyz.openbmc_project.Inventory.Item.Storage"}); |
| dbus::utility::MapperGetSubTreeResponse mockSubtreeResponse{}; |
| std::shared_ptr<ValueType> subtree = |
| managedStore::MockManagedStoreTest::CreateValueType( |
| std::move(mockSubtreeResponse)); |
| |
| ASSERT_TRUE( |
| managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(getAssociatedSubTreeKey, subtree) |
| .ok()); |
| } |
| |
| void mockMetricStore(std::string volumePath, bool failed = false) |
| { |
| KeyType key(ManagedType::kManagedProperty, "xyz.openbmc_project.NVMe", |
| volumePath, "xyz.openbmc_project.NVMe.MetricStore", |
| "MetricCollection"); |
| if (failed) |
| { |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->evictMockObjectFromManagedStore(key) |
| .ok()); |
| } |
| else |
| { |
| std::shared_ptr<ValueType> metricCollection = |
| managedStore::MockManagedStoreTest::CreateValueType< |
| dbus::utility::DbusVariantType>(std::move( |
| std::vector<std::string>({"FirstMetric", "SecondMetric"}))); |
| ASSERT_TRUE( |
| managedStore::GetManagedObjectStore() |
| ->upsertMockObjectIntoManagedStore(key, metricCollection) |
| .ok()); |
| } |
| } |
| |
| void checkGenericVolumeMockData(nlohmann::json& json) |
| { |
| EXPECT_EQ(json["@odata.id"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1"); |
| EXPECT_EQ(json["@odata.type"], "#Volume.v1_9_0.Volume"); |
| EXPECT_EQ(json["BlockSizeBytes"], 4096); |
| EXPECT_EQ(json["Capacity"]["Data"]["ProvisionedBytes"], 3221225472000); |
| EXPECT_EQ(json["Id"], "1"); |
| EXPECT_EQ(json["NVMeNamespaceProperties"]["LBAFormat"]["LBADataSizeBytes"], |
| 4096); |
| EXPECT_EQ(json["NVMeNamespaceProperties"]["LBAFormat"]["LBAFormatType"], |
| "LBAFormat0"); |
| EXPECT_EQ(json["NVMeNamespaceProperties"]["NamespaceId"], "0x00000001"); |
| EXPECT_EQ(json["Name"], "Namespace 1"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageVolumeHandlerCheck) |
| { |
| std::string storageName = "storage_nvme"; |
| std::string volumeName = "1"; |
| std::string storagePath; |
| mockStorageObject(storageName, storagePath); |
| std::string volumePath; |
| addVolumeToMockStorage(storagePath, volumeName, volumePath); |
| mockItemVolumeData(volumePath); |
| mockNvmeVolumeData(volumePath); |
| mockMetricStore(volumePath); |
| |
| // Skip location check due to it's complexity and |
| // since this test is targeting Metrics |
| mockAvoidLocationCheckForVolume(volumePath); |
| |
| storageVolumeHandler(app_, CreateRequest(), share_async_resp_, "system", |
| storageName, volumeName); |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| |
| checkGenericVolumeMockData(json); |
| EXPECT_EQ( |
| json["Oem"]["Google"]["FirstMetric"]["DataUri"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1/Oem/Google/Metrics/FirstMetric"); |
| EXPECT_EQ( |
| json["Oem"]["Google"]["SecondMetric"]["DataUri"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1/Oem/Google/Metrics/SecondMetric"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageVolumeHandlerCheckWithoutItemVolume) |
| { |
| std::string storageName = "storage_nvme"; |
| std::string volumeName = "1"; |
| std::string storagePath; |
| mockStorageObject(storageName, storagePath); |
| std::string volumePath; |
| addVolumeToMockStorage(storagePath, volumeName, volumePath); |
| mockNvmeVolumeData(volumePath); |
| mockMetricStore(volumePath); |
| |
| // remove item volume from store |
| // previous testcases can affect this, hence removing |
| KeyType itemVolumeKey(ManagedType::kManagedPropertyMap, |
| "xyz.openbmc_project.NVMe", volumePath, |
| "xyz.openbmc_project.Inventory.Item.Volume"); |
| ASSERT_TRUE(managedStore::GetManagedObjectStore() |
| ->evictMockObjectFromManagedStore(itemVolumeKey) |
| .ok()); |
| // Skip location check due to it's complexity and |
| // since this test is targeting Metrics |
| mockAvoidLocationCheckForVolume(volumePath); |
| |
| storageVolumeHandler(app_, CreateRequest(), share_async_resp_, "system", |
| storageName, volumeName); |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| |
| EXPECT_EQ(json["@odata.id"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1"); |
| EXPECT_EQ(json["@odata.type"], "#Volume.v1_9_0.Volume"); |
| EXPECT_FALSE(json.contains("BlockSizeBytes")); |
| EXPECT_FALSE(json.contains("Capacity")); |
| EXPECT_FALSE(json.contains("NVMeNamespaceProperties")); |
| EXPECT_EQ(json["Id"], "1"); |
| EXPECT_EQ(json["Name"], std::string("Volume ") + volumeName); |
| EXPECT_EQ( |
| json["Oem"]["Google"]["FirstMetric"]["DataUri"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1/Oem/Google/Metrics/FirstMetric"); |
| EXPECT_EQ( |
| json["Oem"]["Google"]["SecondMetric"]["DataUri"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1/Oem/Google/Metrics/SecondMetric"); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageVolumeHandlerCheckWithoutMetricStore) |
| { |
| std::string storageName = "storage_nvme"; |
| std::string volumeName = "1"; |
| std::string storagePath; |
| mockStorageObject(storageName, storagePath); |
| std::string volumePath; |
| addVolumeToMockStorage(storagePath, volumeName, volumePath, false); |
| mockItemVolumeData(volumePath); |
| mockNvmeVolumeData(volumePath); |
| |
| // Skip location check due to it's complexity and |
| // since this test is targeting Metrics |
| mockAvoidLocationCheckForVolume(volumePath); |
| |
| storageVolumeHandler(app_, CreateRequest(), share_async_resp_, "system", |
| storageName, volumeName); |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| |
| checkGenericVolumeMockData(json); |
| EXPECT_FALSE(json["Oem"]["Google"].contains("FirstMetric")); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageVolumeHandlerCheckWithMetricStoreError) |
| { |
| std::string storageName = "storage_nvme"; |
| std::string volumeName = "1"; |
| std::string storagePath; |
| mockStorageObject(storageName, storagePath); |
| std::string volumePath; |
| addVolumeToMockStorage(storagePath, volumeName, volumePath); |
| mockItemVolumeData(volumePath); |
| mockNvmeVolumeData(volumePath); |
| mockMetricStore(volumePath, true); |
| |
| // Skip location check due to it's complexity and |
| // since this test is targeting Metrics |
| mockAvoidLocationCheckForVolume(volumePath); |
| |
| storageVolumeHandler(app_, CreateRequest(), share_async_resp_, "system", |
| storageName, volumeName); |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok); |
| |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| |
| checkGenericVolumeMockData(json); |
| EXPECT_FALSE(json["Oem"]["Google"].contains("FirstMetric")); |
| } |
| |
| TEST_F(StorgeSnapshotFixture, StorageVolumeGetMetricError) |
| { |
| std::string storageName = "storage_nvme"; |
| std::string volumeName = "1"; |
| std::string storagePath; |
| mockStorageObject(storageName, storagePath); |
| std::string volumePath; |
| addVolumeToMockStorage(storagePath, volumeName, volumePath); |
| mockMetricStore(volumePath); |
| std::string metricId = "FirstMetric"; |
| |
| testing::StrictMock<sdbusplus::SdBusMock> sdbus; |
| EXPECT_CALL(sdbus, |
| sd_bus_message_new_method_call(testing::_, testing::_, nullptr, |
| nullptr, nullptr, nullptr)) |
| .WillRepeatedly(testing::Return(0)); |
| sd_bus_error err; |
| err.name = "xyz.openbmc_project.Common.Error.Unavailable"; |
| |
| EXPECT_CALL(sdbus, sd_bus_message_get_error(testing::_)) |
| .WillRepeatedly(testing::Return(&err)); |
| // intentionally failing |
| // want to test only properties not the metric data since there is no |
| // existing model to mock fd |
| sdbusplus::message_t msg = |
| sdbusplus::get_mocked_new(&sdbus).new_method_call(nullptr, nullptr, |
| nullptr, nullptr); |
| EXPECT_CALL( |
| *managedStore::GetManagedObjectStore(), |
| PostDbusCallToIoContextThreadSafe( |
| _, |
| An<absl::AnyInvocable<void( |
| const boost::system::error_code&, const sdbusplus::message_t&, |
| const sdbusplus::message::unix_fd&)>&&>(), |
| "xyz.openbmc_project.NVMe", volumePath, |
| "xyz.openbmc_project.NVMe.MetricStore", "GetMetric", metricId)) |
| .Times(testing::AtMost(1)) |
| .WillOnce( |
| SimulateFailedAsyncPostDbusCallThreadSafeWithMsgAndEmptyValueAction:: |
| SimulateFailedAsyncPostDbusCallWithMsgAndEmptyValue(msg)); |
| handleStorageVolumeMetricGet(share_async_resp_, "system", storageName, |
| volumeName, metricId); |
| RunIoUntilDone(); |
| |
| EXPECT_EQ(share_async_resp_->res.result(), |
| boost::beast::http::status::internal_server_error); |
| nlohmann::json& json = share_async_resp_->res.jsonValue; |
| EXPECT_EQ( |
| json["@odata.id"], |
| "/redfish/v1/Systems/system/Storage/storage_nvme/Volumes/1/Oem/Google/Metrics/FirstMetric"); |
| EXPECT_EQ(json["Name"], "FirstMetric"); |
| } |
| } // namespace |
| } // namespace redfish |