diff --git a/cloud/blockstore/config/disk.proto b/cloud/blockstore/config/disk.proto index 9ef64bdb16d..3127e1f1718 100644 --- a/cloud/blockstore/config/disk.proto +++ b/cloud/blockstore/config/disk.proto @@ -57,7 +57,7 @@ message TFileDeviceArgs // Device pool name. optional string PoolName = 4; - // Serial number. For testing purposes. + // Serial number. For caching purposes. optional string SerialNumber = 5; // Data offset relative to the beginning of the file. diff --git a/cloud/blockstore/libs/storage/disk_agent/disk_agent_actor_init.cpp b/cloud/blockstore/libs/storage/disk_agent/disk_agent_actor_init.cpp index 4688c44c416..29cf360c49e 100644 --- a/cloud/blockstore/libs/storage/disk_agent/disk_agent_actor_init.cpp +++ b/cloud/blockstore/libs/storage/disk_agent/disk_agent_actor_init.cpp @@ -47,7 +47,7 @@ void TDiskAgentActor::InitAgent(const TActorContext& ctx) std::move(rdmaTargetConfig), OldRequestCounters); - auto* actorSystem = ctx.ActorSystem(); + auto* actorSystem = TActivationContext::ActorSystem(); auto replyTo = ctx.SelfID; State->Initialize().Subscribe([=] (auto future) { @@ -61,7 +61,8 @@ void TDiskAgentActor::InitAgent(const TActorContext& ctx) auto response = std::make_unique( std::move(r.Configs), std::move(r.Errors), - std::move(r.ConfigMismatchErrors)); + std::move(r.ConfigMismatchErrors), + std::move(r.DevicesWithNewSerialNumber)); actorSystem->Send( new IEventHandle( diff --git a/cloud/blockstore/libs/storage/disk_agent/disk_agent_private.h b/cloud/blockstore/libs/storage/disk_agent/disk_agent_private.h index addcf457609..daa8a90ed23 100644 --- a/cloud/blockstore/libs/storage/disk_agent/disk_agent_private.h +++ b/cloud/blockstore/libs/storage/disk_agent/disk_agent_private.h @@ -37,16 +37,19 @@ struct TEvDiskAgentPrivate TVector Configs; TVector Errors; TVector ConfigMismatchErrors; + TVector DevicesWithNewSerialNumber; TInitAgentCompleted() = default; TInitAgentCompleted( TVector configs, TVector errors, - TVector configMismatchErrors) + TVector configMismatchErrors, + TVector devicesWithNewSerialNumber) : Configs(std::move(configs)) , Errors(std::move(errors)) , ConfigMismatchErrors(std::move(configMismatchErrors)) + , DevicesWithNewSerialNumber(std::move(devicesWithNewSerialNumber)) {} }; diff --git a/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.cpp b/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.cpp index e452bfe8da3..168cba05b57 100644 --- a/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.cpp +++ b/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.cpp @@ -380,7 +380,7 @@ TFuture TDiskAgentState::InitAioStorage() AgentConfig, StorageProvider, NvmeManager) - .Apply([=] (auto future) { + .Apply([=, this] (auto future) { TInitializeStorageResult r = future.ExtractValue(); InitErrorsCount = r.Errors.size(); @@ -412,12 +412,13 @@ TFuture TDiskAgentState::InitAioStorage() std::move(device)); } - return TInitializeResult { + return TInitializeResult{ .Configs = std::move(r.Configs), .Errors = std::move(r.Errors), .ConfigMismatchErrors = std::move(r.ConfigMismatchErrors), - .Guard = std::move(r.Guard) - }; + .DevicesWithNewSerialNumber = + std::move(r.DevicesWithNewSerialNumber), + .Guard = std::move(r.Guard)}; }); } diff --git a/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.h b/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.h index 3cb018e3309..c2b1cf6fc9d 100644 --- a/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.h +++ b/cloud/blockstore/libs/storage/disk_agent/disk_agent_state.h @@ -86,6 +86,8 @@ class TDiskAgentState TVector Configs; TVector Errors; TVector ConfigMismatchErrors; + TVector DevicesWithNewSerialNumber; + TDeviceGuard Guard; }; diff --git a/cloud/blockstore/libs/storage/disk_agent/storage_initializer.cpp b/cloud/blockstore/libs/storage/disk_agent/storage_initializer.cpp index ee19cdf04aa..0b5d6cff718 100644 --- a/cloud/blockstore/libs/storage/disk_agent/storage_initializer.cpp +++ b/cloud/blockstore/libs/storage/disk_agent/storage_initializer.cpp @@ -109,8 +109,11 @@ class TInitializer TVector Errors; TVector ConfigMismatchErrors; + TVector DevicesWithNewSerialNumber; TMutex Lock; + THashMap PathToSerial; + public: TInitializer( TLog log, @@ -141,11 +144,17 @@ class TInitializer void ScanFileDevices(); bool ValidateGeneratedConfigs(const TVector& fileDevices); bool ValidateStorageDiscoveryConfig() const; - void ValidateCurrentConfigs(); + void ValidateCurrentConfigs( + const TVector& cachedDevices); void SaveCurrentConfig(); void ReportDiskAgentConfigMismatchEvent(const TString& error); + + [[nodiscard]] TString GetCachedConfigsPath() const; + + TString GetSerialNumber(const TString& path); + void SetupSerialNumbers(TVector& fileDevices); }; //////////////////////////////////////////////////////////////////////////////// @@ -291,6 +300,33 @@ bool TInitializer::ValidateGeneratedConfigs( return true; } +TString TInitializer::GetSerialNumber(const TString& path) +{ + auto it = PathToSerial.find(path); + if (it == PathToSerial.end()) { + auto [sn, error] = NvmeManager->GetSerialNumber(path); + it = PathToSerial.emplace(path, sn).first; + if (HasError(error)) { + with_lock (Lock) { + Errors.push_back( + TStringBuilder() + << "Can't get serial number for " << path.Quote() << ": " + << FormatError(error)); + } + } + } + + return it->second; +} + +void TInitializer::SetupSerialNumbers( + TVector& fileDevices) +{ + for (auto& file: fileDevices) { + file.SetSerialNumber(GetSerialNumber(file.GetPath())); + } +} + void TInitializer::ScanFileDevices() { if (!ValidateStorageDiscoveryConfig()) { @@ -310,12 +346,12 @@ void TInitializer::ScanFileDevices() } TVector files = gen.ExtractResult(); - if (files.empty()) { return; } SortBy(files, GetDeviceId); + SetupSerialNumbers(files); if (!ValidateGeneratedConfigs(files)) { ReportDiskAgentConfigMismatchEvent("Bad generated config"); @@ -350,8 +386,7 @@ void TInitializer::ScanFileDevices() void TInitializer::SaveCurrentConfig() { - const auto path = AgentConfig->GetCachedConfigPath(); - + const auto path = GetCachedConfigsPath(); if (path.empty()) { return; } @@ -378,16 +413,19 @@ void TInitializer::SaveCurrentConfig() } } -void TInitializer::ValidateCurrentConfigs() +TString TInitializer::GetCachedConfigsPath() const { const TString storagePath = StorageConfig->GetCachedDiskAgentConfigPath(); const TString diskAgentPath = AgentConfig->GetCachedConfigPath(); - const TString& path = diskAgentPath.empty() ? storagePath : diskAgentPath; - auto cachedDevices = LoadCachedConfig(path); + return diskAgentPath.empty() ? storagePath : diskAgentPath; +} + +void TInitializer::ValidateCurrentConfigs( + const TVector& cachedDevices) +{ if (cachedDevices.empty()) { STORAGE_INFO("There is no cached config"); SaveCurrentConfig(); - return; } @@ -406,14 +444,15 @@ void TInitializer::ValidateCurrentConfigs() ss << d << "\n"; } ss << "\nCached:\n"; - for (auto& d: cachedDevices) { + for (const auto& d: cachedDevices) { ss << d << "\n"; } ReportDiskAgentConfigMismatchEvent(ss.Str()); STORAGE_WARN("Current config is broken, fallback to the cached one."); - FileDevices.swap(cachedDevices); + FileDevices = cachedDevices; + SetupSerialNumbers(FileDevices); Errors.push_back(TStringBuilder() << "broken config: " << FormatError(error)); @@ -424,7 +463,22 @@ TFuture TInitializer::Initialize() ScanFileDevices(); try { - ValidateCurrentConfigs(); + const auto cachedDevices = LoadCachedConfig(GetCachedConfigsPath()); + for (const auto& d: cachedDevices) { + const auto currentSerialNumber = GetSerialNumber(d.GetPath()); + + if (currentSerialNumber != d.GetSerialNumber()) { + STORAGE_WARN( + "Device " << d.GetDeviceId() << " [" << d.GetPath() + << "] has new serial number " + << TString(currentSerialNumber).Quote() + << " (was " << d.GetSerialNumber().Quote() + << ")"); + DevicesWithNewSerialNumber.push_back(d.GetDeviceId()); + } + } + + ValidateCurrentConfigs(cachedDevices); } catch (...) { return MakeErrorFuture(std::current_exception()); } @@ -470,7 +524,7 @@ TFuture TInitializer::Initialize() device.GetOffset() / device.GetBlockSize(), Configs[i], Stats[i] - ).Subscribe([=] (const auto& future) { + ).Subscribe([=, this] (const auto& future) { try { Devices[i] = future.GetValue(); } catch (...) { @@ -498,7 +552,7 @@ TFuture TInitializer::Initialize() try { auto result = CreateMemoryStorage(Configs[i], Stats[i]) - .Subscribe([=] (const auto& future) { + .Subscribe([=, this] (const auto& future) { try { Devices[i] = future.GetValue(); } catch (...) { @@ -520,32 +574,16 @@ TFuture TInitializer::Initialize() NProto::TDeviceConfig TInitializer::CreateConfig( const NProto::TFileDeviceArgs& device) { - const auto& path = device.GetPath(); - const ui32 blockSize = device.GetBlockSize(); - NProto::TDeviceConfig config; - config.SetDeviceName(path); + config.SetDeviceName(device.GetPath()); config.SetDeviceUUID(device.GetDeviceId()); - config.SetBlockSize(blockSize); + config.SetBlockSize(device.GetBlockSize()); config.SetRack(AgentConfig->GetRack()); config.SetPoolName(device.GetPoolName()); config.SetSerialNumber(device.GetSerialNumber()); config.SetPhysicalOffset(device.GetOffset()); - if (!config.GetSerialNumber()) { - auto [sn, error] = NvmeManager->GetSerialNumber(path); - if (!HasError(error)) { - config.SetSerialNumber(sn); - } else { - with_lock (Lock) { - Errors.push_back(TStringBuilder() - << "Can't get serial number for " << path.Quote() << ": " - << FormatError(error)); - } - } - } - return config; } @@ -587,6 +625,7 @@ TInitializeStorageResult TInitializer::GetResult() r.Errors = std::move(Errors); r.ConfigMismatchErrors = std::move(ConfigMismatchErrors); + r.DevicesWithNewSerialNumber = std::move(DevicesWithNewSerialNumber); r.Guard = std::move(Guard); return r; diff --git a/cloud/blockstore/libs/storage/disk_agent/storage_initializer.h b/cloud/blockstore/libs/storage/disk_agent/storage_initializer.h index a01970afdda..03607dd8bf4 100644 --- a/cloud/blockstore/libs/storage/disk_agent/storage_initializer.h +++ b/cloud/blockstore/libs/storage/disk_agent/storage_initializer.h @@ -29,6 +29,7 @@ struct TInitializeStorageResult TVector Stats; TVector Errors; TVector ConfigMismatchErrors; + TVector DevicesWithNewSerialNumber; TDeviceGuard Guard; }; diff --git a/cloud/blockstore/libs/storage/disk_agent/storage_initializer_ut.cpp b/cloud/blockstore/libs/storage/disk_agent/storage_initializer_ut.cpp new file mode 100644 index 00000000000..872749aafed --- /dev/null +++ b/cloud/blockstore/libs/storage/disk_agent/storage_initializer_ut.cpp @@ -0,0 +1,439 @@ +#include "storage_initializer.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +namespace NCloud::NBlockStore::NStorage { + +using namespace NThreading; + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +struct TTestNvmeManager + : NNvme::INvmeManager +{ + THashMap PathToSerial; + + explicit TTestNvmeManager( + const TVector> pathToSerial) + : PathToSerial{pathToSerial.cbegin(), pathToSerial.cend()} + {} + + TFuture Format( + const TString& path, + NNvme::nvme_secure_erase_setting ses) override + { + Y_UNUSED(path); + Y_UNUSED(ses); + + return MakeFuture(); + } + + TFuture Deallocate( + const TString& path, + ui64 offsetBytes, + ui64 sizeBytes) override + { + Y_UNUSED(path); + Y_UNUSED(offsetBytes); + Y_UNUSED(sizeBytes); + + return MakeFuture(); + } + + TResultOrError GetSerialNumber(const TString& path) override + { + auto it = PathToSerial.find(TFsPath{path}.Basename()); + if (it == PathToSerial.end()) { + return MakeError(MAKE_SYSTEM_ERROR(42)); + } + + return it->second; + } + + TResultOrError IsSsd(const TString& path) override + { + Y_UNUSED(path); + + return true; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct TFixture + : NUnitTest::TBaseFixture +{ + const ui32 DeviceCountPerPath = 8; + const ui32 PathCount = 4; + const ui64 DeviceSize = 64_KB; + const ui64 PaddingSize = 4_KB; + const ui64 HeaderSize = 4_KB; + + const TTempDir TempDir; + const TFsPath DevicesPath = TempDir.Path() / "devices"; + const TFsPath CachedConfigPath = TempDir.Path() / "cache"; + + NProto::TDiskAgentConfig DefaultConfig; + + const ILoggingServicePtr Logging = CreateLoggingService("console"); + + TStorageConfigPtr StorageConfig; + IStorageProviderPtr StorageProvider = NServer::CreateNullStorageProvider(); + + void SetUpStorageDiscoveryConfig() + { + auto& discovery = *DefaultConfig.MutableStorageDiscoveryConfig(); + auto& path = *discovery.AddPathConfigs(); + path.SetPathRegExp(DevicesPath / "NVMENBS([0-9]{2})"); + auto& layout = *path.AddPoolConfigs()->MutableLayout(); + layout.SetDeviceSize(DeviceSize); + layout.SetDevicePadding(PaddingSize); + layout.SetHeaderSize(HeaderSize); + } + + void SetUpStorage() + { + for (ui32 i = 0; i != PathCount; ++i) { + PrepareFile("NVMENBS0" + ToString(i + 1)); + } + } + + void PrepareFile(const TString& name) + { + TFile fileData( + DevicesPath / name, + EOpenModeFlag::CreateNew); + fileData.Resize( + HeaderSize + DeviceCountPerPath * DeviceSize + + (DeviceCountPerPath - 1) * PaddingSize); + } + + void SetUp(NUnitTest::TTestContext&) override + { + DevicesPath.MkDirs(); + CachedConfigPath.MkDirs(); + + DefaultConfig.SetCachedConfigPath(CachedConfigPath / "config.txt"); + DefaultConfig.SetBackend(NProto::DISK_AGENT_BACKEND_AIO); + DefaultConfig.SetAcquireRequired(true); + + SetUpStorageDiscoveryConfig(); + SetUpStorage(); + + StorageConfig = std::make_shared( + NProto::TStorageServiceConfig{}, + std::make_shared()); + } +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +Y_UNIT_TEST_SUITE(TInitializerTest) +{ + Y_UNIT_TEST_F(ShouldInitialize, TFixture) + { + const TVector> pathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "X"}, + {"NVMENBS03", "Y"}, + {"NVMENBS04", "Z"}, + }; + + UNIT_ASSERT(!NFs::Exists(DefaultConfig.GetCachedConfigPath())); + + auto future = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(pathToSerial)); + + const auto& r = future.GetValueSync(); + + UNIT_ASSERT(NFs::Exists(DefaultConfig.GetCachedConfigPath())); + + UNIT_ASSERT_VALUES_EQUAL( + PathCount * DeviceCountPerPath, + r.Configs.size()); + + UNIT_ASSERT_VALUES_EQUAL(r.Configs.size(), r.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(r.Configs.size(), r.Stats.size()); + UNIT_ASSERT_VALUES_EQUAL(0, r.Errors.size()); + UNIT_ASSERT_VALUES_EQUAL(0, r.ConfigMismatchErrors.size()); + UNIT_ASSERT_VALUES_EQUAL(0, r.DevicesWithNewSerialNumber.size()); + + auto configs = r.Configs; + SortBy(configs, [](const auto& d) { return d.GetDeviceName(); }); + for (ui32 i = 0; i != PathCount; ++i) { + const auto& sn = pathToSerial[i].second; + for (ui32 j = 0; j != DeviceCountPerPath; ++j) { + UNIT_ASSERT_VALUES_EQUAL( + sn, + configs[i * DeviceCountPerPath + j].GetSerialNumber()); + } + } + } + + Y_UNIT_TEST_F(ShouldDetectChangeOfSerialNumber, TFixture) + { + const TVector> pathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "X"}, + {"NVMENBS03", "Y"}, + {"NVMENBS04", "Z"}, + }; + + auto future1 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(pathToSerial)); + + const auto& r1 = future1.GetValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(0, r1.DevicesWithNewSerialNumber.size()); + + const TVector> newPathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "new-X"}, + {"NVMENBS03", "Z"}, + {"NVMENBS04", "Y"}, + {"NVMENBS05", "A"}, + }; + + PrepareFile("NVMENBS05"); + + auto future2 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(newPathToSerial)); + + const auto& r2 = future2.GetValueSync(); + + UNIT_ASSERT_VALUES_EQUAL( + DeviceCountPerPath * (PathCount + 1), + r2.Configs.size()); + + UNIT_ASSERT_VALUES_EQUAL(0, r2.ConfigMismatchErrors.size()); + UNIT_ASSERT_VALUES_EQUAL( + DeviceCountPerPath * 3, + r2.DevicesWithNewSerialNumber.size()); + + { + auto configs = r2.Configs; + SortBy(configs, [](const auto& d) { return d.GetDeviceName(); }); + for (ui32 i = 0; i != PathCount; ++i) { + const auto& sn = newPathToSerial[i].second; + for (ui32 j = 0; j != DeviceCountPerPath; ++j) { + UNIT_ASSERT_VALUES_EQUAL( + sn, + configs[i * DeviceCountPerPath + j].GetSerialNumber()); + } + } + } + + auto future3 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(newPathToSerial)); + + const auto& r3 = future3.GetValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(0, r3.ConfigMismatchErrors.size()); + UNIT_ASSERT_VALUES_EQUAL(0, r3.DevicesWithNewSerialNumber.size()); + UNIT_ASSERT_VALUES_EQUAL(r2.Configs.size(), r3.Configs.size()); + UNIT_ASSERT(std::equal( + r2.Configs.cbegin(), + r2.Configs.cend(), + r3.Configs.cbegin(), + r3.Configs.cend(), + [](const auto& lhs, const auto& rhs) + { + return lhs.GetDeviceName() == rhs.GetDeviceName() && + lhs.GetDeviceUUID() == rhs.GetDeviceUUID() && + lhs.GetSerialNumber() == rhs.GetSerialNumber() && + lhs.GetBlockSize() == rhs.GetBlockSize() && + lhs.GetBlocksCount() == rhs.GetBlocksCount() && + lhs.GetPhysicalOffset() == rhs.GetPhysicalOffset(); + })); + } + + Y_UNIT_TEST_F( + ShouldUpdateSerialNumberWhenRestoringConfigFromCache, + TFixture) + { + const TVector> pathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "X"}, + {"NVMENBS03", "Y"}, + {"NVMENBS04", "Z"}, + }; + + UNIT_ASSERT(!NFs::Exists(DefaultConfig.GetCachedConfigPath())); + + auto future1 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(pathToSerial)); + + const auto& r1 = future1.GetValueSync(); + + UNIT_ASSERT(NFs::Exists(DefaultConfig.GetCachedConfigPath())); + + const TVector> newPathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "A"}, + {"NVMENBS03", "Z"}, + {"NVMENBS04", "Y"}, + }; + + auto newConfig = DefaultConfig; + newConfig.MutableStorageDiscoveryConfig() + ->MutablePathConfigs(0) + ->MutablePoolConfigs(0) + ->MutableLayout() + ->SetDevicePadding(PaddingSize * 2); + + auto future2 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(newConfig, "rack"), + StorageProvider, + std::make_shared(newPathToSerial)); + + const auto& r2 = future2.GetValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(1, r2.ConfigMismatchErrors.size()); + UNIT_ASSERT_VALUES_EQUAL( + DeviceCountPerPath * 3, + r2.DevicesWithNewSerialNumber.size()); + UNIT_ASSERT_VALUES_EQUAL(r1.Configs.size(), r2.Configs.size()); + UNIT_ASSERT(std::equal( + r1.Configs.cbegin(), + r1.Configs.cend(), + r2.Configs.cbegin(), + r2.Configs.cend(), + [](const auto& lhs, const auto& rhs) + { + return lhs.GetDeviceName() == rhs.GetDeviceName() && + lhs.GetDeviceUUID() == rhs.GetDeviceUUID() && + lhs.GetBlockSize() == rhs.GetBlockSize() && + lhs.GetBlocksCount() == rhs.GetBlocksCount() && + lhs.GetPhysicalOffset() == rhs.GetPhysicalOffset(); + })); + + { + auto configs = r2.Configs; + SortBy(configs, [](const auto& d) { return d.GetDeviceName(); }); + for (ui32 i = 0; i != PathCount; ++i) { + const auto& sn = newPathToSerial[i].second; + for (ui32 j = 0; j != DeviceCountPerPath; ++j) { + UNIT_ASSERT_VALUES_EQUAL( + sn, + configs[i * DeviceCountPerPath + j].GetSerialNumber()); + } + } + } + } + + Y_UNIT_TEST_F(ShouldUpdateSerialNumberForMissingDevices, TFixture) + { + const TVector> pathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "X"}, + {"NVMENBS03", "Y"}, + {"NVMENBS04", "Z"}, + }; + + auto future1 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(DefaultConfig, "rack"), + StorageProvider, + std::make_shared(pathToSerial)); + + const auto& r1 = future1.GetValueSync(); + + const TVector> newPathToSerial{ + {"NVMENBS01", "W"}, + {"NVMENBS02", "A"}, + {"NVMENBS03", "Z"}, + {"NVMENBS04", "Y"}, + }; + + auto newConfig = DefaultConfig; + newConfig.MutableStorageDiscoveryConfig() + ->MutablePathConfigs(0) + ->SetPathRegExp(DevicesPath / "NVMENBS0([1])"); + + auto future2 = InitializeStorage( + Logging->CreateLog("Test"), + StorageConfig, + std::make_shared(newConfig, "rack"), + StorageProvider, + std::make_shared(newPathToSerial)); + + const auto& r2 = future2.GetValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(1, r2.ConfigMismatchErrors.size()); + UNIT_ASSERT_VALUES_EQUAL( + DeviceCountPerPath * 3, + r2.DevicesWithNewSerialNumber.size()); + UNIT_ASSERT_VALUES_EQUAL(r1.Configs.size(), r2.Configs.size()); + UNIT_ASSERT(std::equal( + r1.Configs.cbegin(), + r1.Configs.cend(), + r2.Configs.cbegin(), + r2.Configs.cend(), + [](const auto& lhs, const auto& rhs) + { + return lhs.GetDeviceName() == rhs.GetDeviceName() && + lhs.GetDeviceUUID() == rhs.GetDeviceUUID() && + lhs.GetBlockSize() == rhs.GetBlockSize() && + lhs.GetBlocksCount() == rhs.GetBlocksCount() && + lhs.GetPhysicalOffset() == rhs.GetPhysicalOffset(); + })); + + { + auto configs = r2.Configs; + SortBy(configs, [](const auto& d) { return d.GetDeviceName(); }); + for (ui32 i = 0; i != PathCount; ++i) { + const auto& sn = newPathToSerial[i].second; + for (ui32 j = 0; j != DeviceCountPerPath; ++j) { + UNIT_ASSERT_VALUES_EQUAL( + sn, + configs[i * DeviceCountPerPath + j].GetSerialNumber()); + } + } + } + } +} + +} // namespace NCloud::NBlockStore::NStorage diff --git a/cloud/blockstore/libs/storage/disk_agent/ut/ya.make b/cloud/blockstore/libs/storage/disk_agent/ut/ya.make index ef4ddad3fcf..2370919d51c 100644 --- a/cloud/blockstore/libs/storage/disk_agent/ut/ya.make +++ b/cloud/blockstore/libs/storage/disk_agent/ut/ya.make @@ -11,6 +11,7 @@ SRCS( rdma_target_ut.cpp recent_blocks_tracker_ut.cpp spdk_initializer_ut.cpp + storage_initializer_ut.cpp storage_with_stats_ut.cpp )