diff --git a/src/VirtualClient/VirtualClient.Actions.UnitTests/Sysbench/SysbenchConfigurationTests.cs b/src/VirtualClient/VirtualClient.Actions.UnitTests/Sysbench/SysbenchConfigurationTests.cs index 529b685442..f7ae18a06d 100644 --- a/src/VirtualClient/VirtualClient.Actions.UnitTests/Sysbench/SysbenchConfigurationTests.cs +++ b/src/VirtualClient/VirtualClient.Actions.UnitTests/Sysbench/SysbenchConfigurationTests.cs @@ -332,7 +332,7 @@ public async Task SysbenchConfigurationProperlyExecutesTPCCConfigurablePreparati string[] expectedCommands = { - $"python3 {this.mockPackagePath}/populate-database.py --dbName sbtest --databaseSystem MySQL --benchmark TPCC --threadCount 16 --tableCount 40 --warehouses 999 --password [A-Za-z0-9+/=]+ --hostIpAddress \"1.2.3.5\"" + $"python3 {this.mockPackagePath}/populate-database.py --dbName sbtest --databaseSystem MySQL --benchmark TPCC --threadCount 16 --tableCount 40 --warehouses 1000 --password [A-Za-z0-9+/=]+ --hostIpAddress \"1.2.3.5\"" }; int commandNumber = 0; @@ -446,7 +446,7 @@ public async Task SysbenchConfigurationProperlyExecutesPostgreSQLTPCCConfigurabl string[] expectedCommands = { - $"python3 {this.mockPackagePath}/populate-database.py --dbName sbtest --databaseSystem PostgreSQL --benchmark TPCC --threadCount 16 --tableCount 40 --warehouses 999 --password [A-Za-z0-9+/=]+ --hostIpAddress \"1.2.3.5\"" + $"python3 {this.mockPackagePath}/populate-database.py --dbName sbtest --databaseSystem PostgreSQL --benchmark TPCC --threadCount 16 --tableCount 40 --warehouses 1000 --password [A-Za-z0-9+/=]+ --hostIpAddress \"1.2.3.5\"" }; int commandNumber = 0; diff --git a/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBClientExecutor.cs b/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBClientExecutor.cs index 579821edfe..4962cbca24 100644 --- a/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBClientExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBClientExecutor.cs @@ -21,6 +21,8 @@ namespace VirtualClient.Actions /// public class HammerDBClientExecutor : HammerDBExecutor { + private static string runTransactionsTclName = "runTransactions.tcl"; + /// /// Initializes a new instance of the class. /// @@ -154,7 +156,7 @@ private Task ExecuteWorkloadAsync(EventContext telemetryContext, CancellationTok using (IProcessProxy process = await this.ExecuteCommandAsync( command, - $"{script} --runTransactionsTCLFilePath {this.RunTransactionsTclName}", + $"{script} --runTransactionsTCLFilePath {runTransactionsTclName}", this.HammerDBPackagePath, telemetryContext, cancellationToken)) diff --git a/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBExecutor.cs b/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBExecutor.cs index 16909ec9c4..f8dc97f78b 100644 --- a/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/HammerDB/HammerDBExecutor.cs @@ -25,6 +25,7 @@ namespace VirtualClient.Actions [SupportedPlatforms("linux-x64")] public class HammerDBExecutor : VirtualClientComponent { + private static string createDBTclName = "createDB.tcl"; private readonly IStateManager stateManager; private static readonly List Factors = new List { 1, 10, 30, 100, 300, 1000, 3000, 10000, 30000, 100000 }; @@ -57,28 +58,6 @@ public string Action } } - /// - /// Defines the name of the createDB TCL file. - /// - public string CreateDBTclName - { - get - { - return "createDB.tcl"; - } - } - - /// - /// Defines the name of the runTransactions TCL file. - /// - public string RunTransactionsTclName - { - get - { - return "runTransactions.tcl"; - } - } - /// /// Defines the name of the PostgreSQL database to create/use for the transactions. /// @@ -137,18 +116,6 @@ public string WarehouseCount } } - /// - /// Disk filter specified - /// - public string DiskFilter - { - get - { - // and 256G - return this.Parameters.GetValue(nameof(this.DiskFilter), "osdisk:false&sizegreaterthan:256g"); - } - } - /// /// Workload duration. /// @@ -341,7 +308,7 @@ protected async Task InitializeExecutablesAsync(EventContext telemetryContext, C private async Task PrepareSQLDatabase(EventContext telemetryContext, CancellationToken cancellationToken) { string command = "python3"; - string arguments = $"{this.PlatformSpecifics.Combine(this.HammerDBPackagePath, "populate-database.py")} --createDBTCLPath {this.CreateDBTclName}"; + string arguments = $"{this.PlatformSpecifics.Combine(this.HammerDBPackagePath, "populate-database.py")} --createDBTCLPath {createDBTclName}"; using (IProcessProxy process = await this.ExecuteCommandAsync( command, @@ -378,17 +345,11 @@ private async Task ConfigureCreateHammerDBFile(EventContext telemetryContext, Ca } } - private async Task GenerateCommandLineArguments(CancellationToken cancellationToken) + private Task GenerateCommandLineArguments(CancellationToken cancellationToken) { string arguments = $"{this.PlatformSpecifics.Combine(this.HammerDBPackagePath, "configure-workload-generator.py")} --workload {this.Workload} --sqlServer {this.SQLServer} --port {this.Port}" + $" --virtualUsers {this.VirtualUsers} --password {this.SuperUserPassword} --dbName {this.DatabaseName} --hostIPAddress {this.ServerIpAddress}"; - if (this.IsMultiRoleLayout() && this.GetLayoutClientInstance().Role == ClientRole.Server) - { - string directories = await this.GetDataDirectoriesAsync(cancellationToken); - arguments = $"{arguments} --directories {directories}"; - } - if (this.Workload.Equals("tpcc", StringComparison.OrdinalIgnoreCase)) { arguments = $"{arguments} --warehouseCount {this.WarehouseCount} --duration {this.Duration.TotalMinutes}"; @@ -407,50 +368,8 @@ private async Task GenerateCommandLineArguments(CancellationToken cancellationTo } this.HammerDBScenarioArguments = arguments; - } - - private async Task GetDataDirectoriesAsync(CancellationToken cancellationToken) - { - string diskPaths = string.Empty; - - if (!cancellationToken.IsCancellationRequested) - { - IEnumerable disks = await this.SystemManager.DiskManager.GetDisksAsync(cancellationToken) - .ConfigureAwait(false); - - if (disks?.Any() != true) - { - throw new WorkloadException( - "Unexpected scenario. The disks defined for the system could not be properly enumerated.", - ErrorReason.WorkloadUnexpectedAnomaly); - } - - IEnumerable disksToTest = DiskFilters.FilterDisks(disks, this.DiskFilter, this.Platform).ToList(); - - if (disksToTest?.Any() != true) - { - throw new WorkloadException( - "Expected disks to test not found. Given the parameters defined for the profile action/step or those passed " + - "in on the command line, the requisite disks do not exist on the system or could not be identified based on the properties " + - "of the existing disks.", - ErrorReason.DependencyNotFound); - } - - foreach (Disk disk in disksToTest) - { - string path = this.Combine(disk.GetPreferredAccessPath(this.Platform), $"{this.SQLServer.ToLower()}"); - - // Create the directory if it doesn't exist - if (!this.SystemManager.FileSystem.Directory.Exists(path)) - { - this.SystemManager.FileSystem.Directory.CreateDirectory(path); - } - - diskPaths += $"{path}:"; - } - } - return diskPaths; + return Task.CompletedTask; } private static Task OpenFirewallPortsAsync(int port, IFirewallManager firewallManager, CancellationToken cancellationToken) diff --git a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchClientExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchClientExecutor.cs index da48ffc57a..2862b60337 100644 --- a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchClientExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchClientExecutor.cs @@ -179,7 +179,7 @@ private Task ExecuteWorkloadAsync(EventContext telemetryContext, CancellationTok { using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken)) { - this.sysbenchLoggingArguments = this.BuildSysbenchLoggingArguments(SysbenchMode.Run); + this.sysbenchLoggingArguments = this.BuildSysbenchLoggingArguments(); this.sysbenchExecutionArguments = $"{this.sysbenchLoggingArguments} --workload {this.Workload} --hostIpAddress {this.ServerIpAddress} --durationSecs {this.Duration.TotalSeconds} --password {this.SuperUserPassword}"; string script = $"{this.SysbenchPackagePath}/run-workload.py "; diff --git a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchConfiguration.cs b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchConfiguration.cs index 012664d1a3..5ebaa198b5 100644 --- a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchConfiguration.cs +++ b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchConfiguration.cs @@ -59,15 +59,12 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel case ConfigurationAction.Cleanup: await this.CleanUpDatabase(telemetryContext, cancellationToken); break; - case ConfigurationAction.CreateTables: - await this.PrepareDatabase(telemetryContext, cancellationToken); - break; case ConfigurationAction.PopulateTables: await this.PopulateDatabase(telemetryContext, cancellationToken); break; default: throw new DependencyException( - $"The specified Sysbench action '{this.Action}' is not supported. Supported actions include: \"{ConfigurationAction.PopulateTables}, {ConfigurationAction.Cleanup}, {ConfigurationAction.CreateTables}\".", + $"The specified Sysbench action '{this.Action}' is not supported. Supported actions include: \"{ConfigurationAction.PopulateTables}, {ConfigurationAction.Cleanup}\".", ErrorReason.NotSupported); } } @@ -106,41 +103,6 @@ private async Task CleanUpDatabase(EventContext telemetryContext, CancellationTo await this.stateManager.SaveStateAsync(nameof(SysbenchState), state, cancellationToken); } - private async Task PrepareDatabase(EventContext telemetryContext, CancellationToken cancellationToken) - { - SysbenchState state = await this.stateManager.GetStateAsync(nameof(SysbenchState), cancellationToken) - ?? new SysbenchState(); - - if (!state.DatabasePopulated) - { - string serverIp = (this.IsMultiRoleLayout() && this.IsInRole(ClientRole.Client)) ? this.ServerIpAddress : "localhost"; - string sysbenchPrepareArguments = $"{this.BuildSysbenchLoggingArguments(SysbenchMode.Prepare)} --password {this.SuperUserPassword} --hostIpAddress \"{serverIp}\""; - - string command = $"{this.SysbenchPackagePath}/populate-database.py"; - - using (IProcessProxy process = await this.ExecuteCommandAsync( - SysbenchExecutor.PythonCommand, - $"{command} {sysbenchPrepareArguments}", - this.SysbenchPackagePath, - telemetryContext, - cancellationToken)) - { - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "Sysbench", logToFile: true); - process.ThrowIfErrored(process.StandardError.ToString(), ErrorReason.WorkloadUnexpectedAnomaly); - } - } - } - else - { - throw new DependencyException( - $"Database preparation failed. A database has already been populated on the system. Please drop the tables, or run \"{ConfigurationAction.Cleanup}\" Action" + - $"before attempting to create new tables on this database.", - ErrorReason.NotSupported); - } - } - private async Task PopulateDatabase(EventContext telemetryContext, CancellationToken cancellationToken) { SysbenchState state = await this.stateManager.GetStateAsync(nameof(SysbenchState), cancellationToken) @@ -152,7 +114,7 @@ await this.Logger.LogMessageAsync($"{this.TypeName}.PopulateDatabase", telemetry { string serverIp = (this.IsMultiRoleLayout() && this.IsInRole(ClientRole.Client)) ? this.ServerIpAddress : "localhost"; - string sysbenchLoggingArguments = this.BuildSysbenchLoggingArguments(SysbenchMode.Populate); + string sysbenchLoggingArguments = this.BuildSysbenchLoggingArguments(); this.sysbenchPopulationArguments = $"{sysbenchLoggingArguments} --password {this.SuperUserPassword} --hostIpAddress \"{serverIp}\""; string script = $"{this.SysbenchPackagePath}/populate-database.py"; @@ -227,11 +189,6 @@ private void AddPopulationDurationMetric(string arguments, IProcessProxy process /// internal class ConfigurationAction { - /// - /// Initializes the tables on the database. - /// - public const string CreateTables = nameof(CreateTables); - /// /// Creates Database on MySQL server and Users on Server and any Clients. /// diff --git a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchExecutor.cs b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchExecutor.cs index 2215079023..85d2d4e0fb 100644 --- a/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/Sysbench/SysbenchExecutor.cs @@ -60,27 +60,6 @@ public SysbenchExecutor(IServiceCollection dependencies, IDictionary - /// Defines the mode in which Sysbench is operating. - /// - protected internal enum SysbenchMode - { - /// - /// Creates the database schema with minimal data. - /// - Prepare, - - /// - /// Populates the database with the full dataset. - /// - Populate, - - /// - /// Runs the benchmark workload. - /// - Run - } - /// /// The benchmark (e.g. OLTP, TPCC). /// @@ -409,7 +388,7 @@ protected async Task InitializeExecutablesAsync(EventContext telemetryContext, C /// dbName, databaseSystem, benchmark and tableCount. /// /// - protected string BuildSysbenchLoggingArguments(SysbenchMode mode) + protected string BuildSysbenchLoggingArguments() { int tableCount = GetTableCount(this.DatabaseScenario, this.TableCount, this.Workload); int threadCount = GetThreadCount(this.SystemManager, this.DatabaseScenario, this.Threads); @@ -419,19 +398,11 @@ protected string BuildSysbenchLoggingArguments(SysbenchMode mode) switch (this.Benchmark) { case BenchmarkName.OLTP: - int recordCount = mode == SysbenchMode.Prepare ? 1 : GetRecordCount(this.SystemManager, this.DatabaseScenario, this.RecordCount); + int recordCount = GetRecordCount(this.SystemManager, this.DatabaseScenario, this.RecordCount); loggingArguments = $"{loggingArguments} --recordCount {recordCount}"; break; case BenchmarkName.TPCC: - int warehouseEstimate = GetWarehouseCount(this.SystemManager, this.DatabaseScenario, this.WarehouseCount); - int warehouseCount = mode switch - { - SysbenchMode.Prepare => 1, - SysbenchMode.Populate => Math.Max(1, warehouseEstimate - 1), - SysbenchMode.Run => warehouseEstimate, - _ => warehouseEstimate - }; - + int warehouseCount = GetWarehouseCount(this.SystemManager, this.DatabaseScenario, this.WarehouseCount); loggingArguments = $"{loggingArguments} --warehouses {warehouseCount}"; break; default: diff --git a/src/VirtualClient/VirtualClient.Contracts.UnitTests/DiskFiltersTests.cs b/src/VirtualClient/VirtualClient.Contracts.UnitTests/DiskFiltersTests.cs index a6a9179616..887a98559b 100644 --- a/src/VirtualClient/VirtualClient.Contracts.UnitTests/DiskFiltersTests.cs +++ b/src/VirtualClient/VirtualClient.Contracts.UnitTests/DiskFiltersTests.cs @@ -475,6 +475,107 @@ public void DiskFiltersCanFilterOnDiskPathInWindowsWhenDisksHaveNoVolumePartitio Assert.IsTrue(object.ReferenceEquals(this.disks.ElementAt(2), result.ElementAt(1))); } + [Test] + public void DiskFiltersCanFilterOnAccessPathInLinux() + { + this.disks = this.mockFixture.CreateDisks(PlatformID.Unix, true); + + // Add a striped volume disk with a raid0 access path + Disk stripedDisk = new Disk( + 4, + "/dev/dm-0", + new List + { + new DiskVolume( + 0, + "/dev/dm-0", + new List { "/home/user/mnt_raid0" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + List allDisks = new List(this.disks) { stripedDisk }; + + string filterString = "AccessPath:raid0"; + IEnumerable result = DiskFilters.FilterDisks(allDisks, filterString, PlatformID.Unix); + Assert.AreEqual(1, result.Count()); + Assert.IsTrue(object.ReferenceEquals(stripedDisk, result.First())); + } + + [Test] + public void DiskFiltersAccessPathFilterIsCaseInsensitive() + { + this.disks = this.mockFixture.CreateDisks(PlatformID.Unix, true); + + Disk stripedDisk = new Disk( + 4, + "/dev/dm-0", + new List + { + new DiskVolume( + 0, + "/dev/dm-0", + new List { "/home/user/mnt_RAID0" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + List allDisks = new List(this.disks) { stripedDisk }; + + string filterString = "AccessPath:raid0"; + IEnumerable result = DiskFilters.FilterDisks(allDisks, filterString, PlatformID.Unix); + Assert.AreEqual(1, result.Count()); + Assert.IsTrue(object.ReferenceEquals(stripedDisk, result.First())); + } + + [Test] + public void DiskFiltersCanFilterOnLogicalDisks() + { + this.disks = this.mockFixture.CreateDisks(PlatformID.Unix, true); + + Disk lvmDisk = new Disk( + 4, + "/dev/dm-0", + new List + { + new DiskVolume( + 0, + "/dev/dm-0", + new List { "/home/user/mnt_raid0" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + Disk mapperDisk = new Disk( + 5, + "/dev/mapper/vg0-lv0", + new List + { + new DiskVolume( + 0, + "/dev/mapper/vg0-lv0", + new List { "/mnt/data" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + List allDisks = new List(this.disks) { lvmDisk, mapperDisk }; + + string filterString = "Logical"; + IEnumerable result = DiskFilters.FilterDisks(allDisks, filterString, PlatformID.Unix); + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.Any(d => object.ReferenceEquals(d, lvmDisk))); + Assert.IsTrue(result.Any(d => object.ReferenceEquals(d, mapperDisk))); + } + [Test] public void DiskFiltersHandlesAnomaliesEncounters_1() { diff --git a/src/VirtualClient/VirtualClient.Contracts/DiskFilters.cs b/src/VirtualClient/VirtualClient.Contracts/DiskFilters.cs index 9fa9682e54..e819e64b1e 100644 --- a/src/VirtualClient/VirtualClient.Contracts/DiskFilters.cs +++ b/src/VirtualClient/VirtualClient.Contracts/DiskFilters.cs @@ -86,6 +86,14 @@ public static IEnumerable FilterDisks(IEnumerable disks, string filt disks = DiskFilters.DiskPathFilter(disks, filterValue); break; + case Filters.AccessPath: + disks = DiskFilters.AccessPathFilter(disks, filterValue); + break; + + case Filters.Logical: + disks = DiskFilters.LogicalDiskFilter(disks); + break; + default: throw new EnvironmentSetupException($"Disk filter '{filter}' is not supported.", ErrorReason.DiskInformationNotAvailable); } @@ -144,12 +152,29 @@ private static IEnumerable DiskPathFilter(IEnumerable disks, string return disks; } + private static IEnumerable AccessPathFilter(IEnumerable disks, string accessPathPattern) + { + // Find disks where any volume has an access path containing the given pattern. + disks = disks.Where(d => d.Volumes.Any(v => v.AccessPaths.Any( + p => p.Contains(accessPathPattern, StringComparison.OrdinalIgnoreCase)))); + return disks; + } + + private static IEnumerable LogicalDiskFilter(IEnumerable disks) + { + // LVM device mapper paths: /dev/dm-N or /dev/mapper/* + disks = disks.Where(d => + d.DevicePath?.StartsWith("/dev/dm", StringComparison.OrdinalIgnoreCase) == true + || d.DevicePath?.StartsWith("/dev/mapper", StringComparison.OrdinalIgnoreCase) == true); + return disks; + } + private static IEnumerable FilterStoragePathByPrefix(IEnumerable disks, PlatformID platform) { if (platform == PlatformID.Unix) { // There are NVMe disks that show up in lshw output, that are not really storage devices. This filter filters by common prefixes. - List validPrefixes = new List { "/dev/hd", "/dev/sd", "/dev/nvme", "/dev/xvd" }; + List validPrefixes = new List { "/dev/hd", "/dev/sd", "/dev/nvme", "/dev/xvd", "/dev/dm", "/dev/mapper" }; // Match for either accessPath or devicePath. disks = disks.Where(d => validPrefixes.Any(vp => d.DevicePath?.Trim().StartsWith(vp, StringComparison.OrdinalIgnoreCase) == true)); @@ -248,6 +273,16 @@ private static class Filters /// Disk path filter. /// public const string DiskPath = "diskpath"; + + /// + /// Access path filter. Matches disks with a volume access path containing the given value. + /// + public const string AccessPath = "accesspath"; + + /// + /// Logical disk filter. Matches LVM device mapper disks (/dev/dm-*, /dev/mapper/*). + /// + public const string Logical = "logical"; } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/MySqlServer/MySQLServerConfigurationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/MySqlServer/MySQLServerConfigurationTests.cs index 117a8cd906..d1c7bf47ea 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/MySqlServer/MySQLServerConfigurationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/MySqlServer/MySQLServerConfigurationTests.cs @@ -46,8 +46,24 @@ public void SetUpDefaultBehavior() this.fixture.Directory.Setup(d => d.Exists(It.IsAny())).Returns(true); - IEnumerable disks; - disks = this.fixture.CreateDisks(PlatformID.Unix, true); + // Simulate the LVM striped volume that StripeDisks creates, with a raid0 access path. + Disk stripedDisk = new Disk( + 4, + "/dev/dm-0", + new List + { + new DiskVolume( + 0, + "/dev/dm-0", + new List { "/home/user/mnt_raid0" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + List disks = new List(this.fixture.CreateDisks(PlatformID.Unix, true)); + disks.Add(stripedDisk); this.fixture.DiskManager.Setup(mgr => mgr.GetDisksAsync(It.IsAny())).ReturnsAsync(() => disks); } @@ -58,7 +74,7 @@ public async Task MySQLConfigurationExecutesTheExpectedProcessForConfigureServer string[] expectedCommands = { - $"python3 {this.packagePath}/configure.py --serverIp 1.2.3.4 --innoDbDirs \"/home/user/mnt_dev_sdc1/mysql;/home/user/mnt_dev_sdd1/mysql;/home/user/mnt_dev_sde1/mysql\"", + $"python3 {this.packagePath}/configure.py --serverIp 1.2.3.4 --innoDbDirs \"/home/user/mnt_raid0/mysql\"", }; int commandNumber = 0; @@ -115,7 +131,7 @@ public async Task MySQLConfigurationExecutesTheExpectedProcessForConfigureServer string[] expectedCommands = { - $"python3 {this.packagePath}/configure.py --serverIp 1.2.3.4 --innoDbDirs \"/home/user/mnt_dev_sdc1/mysql;/home/user/mnt_dev_sdd1/mysql;/home/user/mnt_dev_sde1/mysql\" " + + $"python3 {this.packagePath}/configure.py --serverIp 1.2.3.4 --innoDbDirs \"/home/user/mnt_raid0/mysql\" " + $"--inMemory 8192", }; @@ -317,60 +333,6 @@ public async Task MySQLConfigurationExecutesTheExpectedProcessForRaiseMaxStateme } } - [Test] - public async Task MySQLConfigurationExecutesTheExpectedProcessForDistributeDatabaseCommand() - { - this.fixture.Parameters["Action"] = "DistributeDatabase"; - this.fixture.Parameters["DatabaseName"] = "mysql-test"; - this.fixture.Parameters["TableCount"] = "10"; - - string[] expectedCommands = - { - $"python3 {this.packagePath}/distribute-database.py --dbName mysql-test --directories \"/home/user/mnt_dev_sdc1/mysql;/home/user/mnt_dev_sdd1/mysql;/home/user/mnt_dev_sde1/mysql\"", - }; - - int commandNumber = 0; - bool commandExecuted = false; - - this.fixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => - { - string expectedCommand = expectedCommands[commandNumber]; - - if (expectedCommand == $"{exe} {arguments}") - { - commandExecuted = true; - } - - Assert.IsTrue(commandExecuted); - commandExecuted = false; - commandNumber++; - - InMemoryProcess process = new InMemoryProcess - { - StartInfo = new ProcessStartInfo - { - FileName = exe, - Arguments = arguments - }, - ExitCode = 0, - OnStart = () => true, - OnHasExited = () => true - }; - - return process; - }; - - this.fixture.StateManager.OnSaveState((stateId, state) => - { - Assert.IsNotNull(state); - }); - - using (TestMySQLServerConfiguration component = new TestMySQLServerConfiguration(this.fixture)) - { - await component.ExecuteAsync(CancellationToken.None).ConfigureAwait(false); - } - } - private class TestMySQLServerConfiguration : MySQLServerConfiguration { public TestMySQLServerConfiguration(MockFixture fixture) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/PostgreSQLServer/PostgreSQLServerConfigurationTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/PostgreSQLServer/PostgreSQLServerConfigurationTests.cs index 73663a6427..c0f0504fee 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/PostgreSQLServer/PostgreSQLServerConfigurationTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/PostgreSQLServer/PostgreSQLServerConfigurationTests.cs @@ -49,8 +49,24 @@ public void SetupTest(PlatformID platform, Architecture architecture) this.mockFixture.PackageManager.OnGetPackage().ReturnsAsync(this.mockPackage); this.packagePath = this.mockFixture.ToPlatformSpecificPath(this.mockPackage, platform, architecture).Path; - IEnumerable disks; - disks = this.mockFixture.CreateDisks(platform, true); + // Simulate the LVM striped volume that StripeDisks creates, with a raid0 access path. + Disk stripedDisk = new Disk( + 4, + "/dev/dm-0", + new List + { + new DiskVolume( + 0, + "/dev/dm-0", + new List { "/home/user/mnt_raid0" }, + properties: new Dictionary + { + { "size", "1234567890123" } + }) + }); + + List disks = new List(this.mockFixture.CreateDisks(platform, true)); + disks.Add(stripedDisk); this.mockFixture.DiskManager.Setup(mgr => mgr.GetDisksAsync(It.IsAny())).ReturnsAsync(() => disks); } @@ -75,7 +91,7 @@ public async Task PostgreSQLServerConfigurationExecutesTheExpectedProcessForConf string[] expectedCommands = { - $"python3 {tempPackagePath}/configure-server.py --dbName hammerdbtest --serverIp 1.2.3.5 --password [A-Za-z0-9+/=]+ --port 5432 --inMemory [0-9]+", + $"python3 {tempPackagePath}/configure-server.py --dbName hammerdbtest --serverIp 1.2.3.5 --password [A-Za-z0-9+/=]+ --port 5432 --inMemory [0-9]+ --directory \\S+", }; int commandNumber = 0; @@ -174,60 +190,6 @@ public async Task PostgreSQLConfigurationSkipsDatabaseCreationWhenOneExists(Plat Assert.AreEqual(0, commandsExecuted); } - [Test] - [TestCase(PlatformID.Unix, Architecture.X64)] - [TestCase(PlatformID.Win32NT, Architecture.X64)] - public async Task PostgreSQLServerConfigurationExecutesTheExpectedProcessForDistributeDatabaseCommand(PlatformID platform, Architecture architecture) - { - this.SetupTest(platform, architecture); - this.mockFixture.Parameters["Action"] = "DistributeDatabase"; - string expectedCommand; - - if (platform == PlatformID.Unix) - { - expectedCommand = - $"python3 {this.packagePath}/distribute-database.py " + - $"--dbName hammerdbtest " + - $"--directories \"/home/user/mnt_dev_sdc1/postgresql;/home/user/mnt_dev_sdd1/postgresql;/home/user/mnt_dev_sde1/postgresql;\" " + - $"--password [A-Za-z0-9+/=]+"; - } - else - { - string tempPackagePath = this.packagePath.Replace(@"\", @"\\"); - expectedCommand = $"python3 {tempPackagePath}/distribute-database.py --dbName hammerdbtest --directories \"D:\\\\postgresql;E:\\\\postgresql;F:\\\\postgresql;\" --password [A-Za-z0-9+/=]+"; - } - - this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) => - { - string executedCommand = $"{exe} {arguments}"; - Assert.IsTrue(Regex.IsMatch(executedCommand, expectedCommand)); - - InMemoryProcess process = new InMemoryProcess - { - StartInfo = new ProcessStartInfo - { - FileName = exe, - Arguments = arguments - }, - ExitCode = 0, - OnStart = () => true, - OnHasExited = () => true - }; - - return process; - }; - - this.mockFixture.StateManager.OnSaveState((stateId, state) => - { - Assert.IsNotNull(state); - }); - - using (TestPostgreSQLServerConfiguration component = new TestPostgreSQLServerConfiguration(this.mockFixture)) - { - await component.ExecuteAsync(CancellationToken.None).ConfigureAwait(false); - } - } - private class TestPostgreSQLServerConfiguration : PostgreSQLServerConfiguration { public TestPostgreSQLServerConfiguration(MockFixture fixture) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/StripeDisksTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/StripeDisksTests.cs index 5558f26c5f..43790afa39 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/StripeDisksTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/StripeDisksTests.cs @@ -289,6 +289,30 @@ public async Task StripeDisksExecutesTheExpectedCommandOnWindows() Assert.IsTrue(confirmed); } + [Test] + public async Task StripeDisksSkipsWhenMountDirectoryAlreadyHasContent() + { + this.mockFixture.Parameters["DiskFilter"] = "OSDisk:false"; + + string expectedMountDir = $"/home/{Environment.UserName}/mnt_raid0"; + this.mockFixture.Directory.Setup(d => d.Exists(expectedMountDir)).Returns(true); + this.mockFixture.Directory.Setup(d => d.EnumerateFileSystemEntries(expectedMountDir)) + .Returns(new[] { $"{expectedMountDir}/lost+found" }); + + int commandsExecuted = 0; + this.mockFixture.ProcessManager.OnProcessCreated = (process) => + { + commandsExecuted++; + }; + + using (StripeDisks component = new StripeDisks(this.mockFixture.Dependencies, this.mockFixture.Parameters)) + { + await component.ExecuteAsync(CancellationToken.None); + } + + Assert.AreEqual(0, commandsExecuted); + } + private void SetupSystemConfigPackage() { this.systemConfigPackage = new DependencyPath( diff --git a/src/VirtualClient/VirtualClient.Dependencies/MySqlServer/MySqlServerConfiguration.cs b/src/VirtualClient/VirtualClient.Dependencies/MySqlServer/MySqlServerConfiguration.cs index 380a0ba45c..0a57b1b864 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/MySqlServer/MySqlServerConfiguration.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/MySqlServer/MySqlServerConfiguration.cs @@ -63,22 +63,6 @@ public bool SkipInitialize } } - /// - /// stripedisk mount point. - /// - public string StripeDiskMountPoint - { - get - { - if (this.Parameters.TryGetValue(nameof(this.StripeDiskMountPoint), out IConvertible stripediskmountpoint) && stripediskmountpoint != null) - { - return stripediskmountpoint.ToString(); - } - - return string.Empty; - } - } - /// /// Disk filter specified /// @@ -86,8 +70,7 @@ public string DiskFilter { get { - // and 256G - return this.Parameters.GetValue(nameof(this.DiskFilter), "osdisk:false&sizegreaterthan:256g"); + return this.Parameters.GetValue(nameof(this.DiskFilter), "Logical"); } } @@ -134,7 +117,6 @@ public bool InMemory /// protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) { - ProcessManager manager = this.SystemManager.ProcessManager; string stateId = $"{nameof(MySQLServerConfiguration)}-{this.Action}-action-success"; ConfigurationState configurationState = await this.stateManager.GetStateAsync(stateId, cancellationToken) .ConfigureAwait(false); @@ -161,10 +143,6 @@ await this.ConfigureMySQLServerAsync(telemetryContext, cancellationToken) await this.CreateMySQLServerDatabaseAsync(telemetryContext, cancellationToken) .ConfigureAwait(false); break; - case ConfigurationAction.DistributeDatabase: - await this.DistributeMySQLDatabaseAsync(telemetryContext, cancellationToken) - .ConfigureAwait(false); - break; case ConfigurationAction.SetGlobalVariables: await this.SetMySQLGlobalVariableAsync(telemetryContext, cancellationToken) .ConfigureAwait(false); @@ -182,7 +160,7 @@ private async Task ConfigureMySQLServerAsync(EventContext telemetryContext, Canc .FirstOrDefault()?.IPAddress ?? IPAddress.Loopback.ToString(); - string innoDbDirs = !string.IsNullOrEmpty(this.StripeDiskMountPoint) ? this.StripeDiskMountPoint : await this.GetMySQLInnodbDirectoriesAsync(cancellationToken); + string innoDbDirs = await this.GetMySQLInnodbDirectoriesAsync(cancellationToken); string arguments = $"{this.packageDirectory}/configure.py --serverIp {serverIp} --innoDbDirs \"{innoDbDirs}\""; @@ -251,69 +229,96 @@ private async Task SetMySQLGlobalVariableAsync(EventContext telemetryContext, Ca } } - private async Task DistributeMySQLDatabaseAsync(EventContext telemetryContext, CancellationToken cancellationToken) + private async Task GetMySQLInnodbDirectoriesAsync(CancellationToken cancellationToken) { - string innoDbDirs = await this.GetMySQLInnodbDirectoriesAsync(cancellationToken); + IEnumerable disks = await this.SystemManager.DiskManager.GetDisksAsync(cancellationToken) + .ConfigureAwait(false); - string arguments = $"{this.packageDirectory}/distribute-database.py --dbName {this.DatabaseName} --directories \"{innoDbDirs}\""; + IEnumerable filteredDisks = DiskFilters.FilterDisks(disks, this.DiskFilter, this.Platform); - using (IProcessProxy process = await this.ExecuteCommandAsync( - PythonCommand, - arguments, - Environment.CurrentDirectory, - telemetryContext, - cancellationToken)) + this.Logger.LogTraceMessage($"{this.TypeName}: Total disks discovered: {disks.Count()}. Disks after filtering ('{this.DiskFilter}'): {filteredDisks.Count()}."); + + string accessPath = filteredDisks + .SelectMany(d => d.Volumes) + .SelectMany(v => v.AccessPaths) + .FirstOrDefault(); + + // lshw typically does not report LVM logical volumes (/dev/dm-*, /dev/mapper/*). + // Check /proc/mounts directly for any LVM-backed mount point as a fallback. + if (string.IsNullOrEmpty(accessPath) && this.Platform != PlatformID.Win32NT) { - if (!cancellationToken.IsCancellationRequested) + accessPath = await this.FindLvmMountPathAsync(cancellationToken).ConfigureAwait(false); + } + + // No logical volume found — fall back to the biggest non-OS physical disk. + if (string.IsNullOrEmpty(accessPath)) + { + const string physicalDiskFilter = "OsDisk:false&BiggestSize"; + IEnumerable physicalDisks = DiskFilters.FilterDisks(disks, physicalDiskFilter, this.Platform); + + this.Logger.LogTraceMessage($"{this.TypeName}: No logical volume found. Falling back to physical disk filter ('{physicalDiskFilter}'): {physicalDisks.Count()} disk(s)."); + + try { - await this.LogProcessDetailsAsync(process, telemetryContext, "MySQLServerConfiguration", logToFile: true); - process.ThrowIfDependencyInstallationFailed(process.StandardError.ToString()); + accessPath = physicalDisks.FirstOrDefault()?.GetPreferredAccessPath(this.Platform); + } + catch (Exception) + { + // The disk may not have any eligible volumes. } } - } - private async Task GetMySQLInnodbDirectoriesAsync(CancellationToken cancellationToken) - { - string diskPaths = string.Empty; + if (string.IsNullOrEmpty(accessPath)) + { + throw new DependencyException( + "Expected disks not found. Given the parameters defined for the profile action/step or those passed " + + "in on the command line, the requisite disks do not exist on the system or could not be identified based on the properties " + + "of the existing disks.", + ErrorReason.DependencyNotFound); + } + + string mysqlPath = this.Combine(accessPath, "mysql"); - if (!cancellationToken.IsCancellationRequested) + if (!this.SystemManager.FileSystem.Directory.Exists(mysqlPath)) { - IEnumerable disks = await this.SystemManager.DiskManager.GetDisksAsync(cancellationToken) - .ConfigureAwait(false); + this.Logger.LogTraceMessage($"{this.TypeName}: Creating MySQL InnoDB directory '{mysqlPath}'."); + this.SystemManager.FileSystem.Directory.CreateDirectory(mysqlPath); + } - if (disks?.Any() != true) - { - throw new WorkloadException( - "Unexpected scenario. The disks defined for the system could not be properly enumerated.", - ErrorReason.WorkloadUnexpectedAnomaly); - } + this.Logger.LogTraceMessage($"{this.TypeName}: MySQL InnoDB directory resolved to '{mysqlPath}'."); - IEnumerable disksToTest = DiskFilters.FilterDisks(disks, this.DiskFilter, this.Platform).ToList(); + return mysqlPath; + } - if (disksToTest?.Any() != true) - { - throw new WorkloadException( - "Expected disks to test not found. Given the parameters defined for the profile action/step or those passed " + - "in on the command line, the requisite disks do not exist on the system or could not be identified based on the properties " + - "of the existing disks.", - ErrorReason.DependencyNotFound); - } + /// + /// Reads /proc/mounts to find a mount point backed by an LVM device (/dev/mapper/* or /dev/dm-*). + /// Returns the mount path (e.g. /home/user/mnt_raid0) or null if none is found. + /// + private async Task FindLvmMountPathAsync(CancellationToken cancellationToken) + { + try + { + string procMounts = await this.SystemManager.FileSystem.File.ReadAllTextAsync("/proc/mounts", cancellationToken) + .ConfigureAwait(false); - foreach (Disk disk in disksToTest) + foreach (string line in procMounts.Split('\n', StringSplitOptions.RemoveEmptyEntries)) { - string mysqlPath = this.Combine(disk.GetPreferredAccessPath(this.Platform), "mysql"); - - // Create the directory if it doesn't exist - if (!this.SystemManager.FileSystem.Directory.Exists(mysqlPath)) + string[] parts = line.Split(' '); + if (parts.Length >= 2 + && (parts[0].StartsWith("/dev/mapper/", StringComparison.OrdinalIgnoreCase) + || parts[0].StartsWith("/dev/dm-", StringComparison.OrdinalIgnoreCase))) { - this.SystemManager.FileSystem.Directory.CreateDirectory(mysqlPath); + this.Logger.LogTraceMessage($"{this.TypeName}: Found LVM mount from /proc/mounts: device='{parts[0]}', path='{parts[1]}'."); + return parts[1]; } - - diskPaths += $"{mysqlPath};"; } } + catch (Exception ex) + { + this.Logger.LogTraceMessage($"{this.TypeName}: Could not read /proc/mounts: {ex.Message}"); + } - return diskPaths.TrimEnd(';'); + return null; } private async Task GetMySQLInMemoryCapacityAsync(CancellationToken cancellationToken) @@ -360,11 +365,6 @@ internal class ConfigurationAction /// ie. "MAX_PREPARED_STMT_COUNT=1000000;MAX_CONNECTIONS=1024" /// public const string SetGlobalVariables = nameof(SetGlobalVariables); - - /// - /// Distributes existing database to disks on the system - /// - public const string DistributeDatabase = nameof(DistributeDatabase); } internal class ConfigurationState diff --git a/src/VirtualClient/VirtualClient.Dependencies/PostgreSQLServer/PostgreSQLServerConfiguration.cs b/src/VirtualClient/VirtualClient.Dependencies/PostgreSQLServer/PostgreSQLServerConfiguration.cs index f05e6bc593..032411475d 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/PostgreSQLServer/PostgreSQLServerConfiguration.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/PostgreSQLServer/PostgreSQLServerConfiguration.cs @@ -22,6 +22,7 @@ namespace VirtualClient.Dependencies /// /// Provides functionality for configuring PostgreSQL Server. /// + [SupportedPlatforms("linux-x64,linux-arm64")] public class PostgreSQLServerConfiguration : ExecuteCommand { private const string PythonCommand = "python3"; @@ -29,7 +30,7 @@ public class PostgreSQLServerConfiguration : ExecuteCommand private string packageDirectory; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// An enumeration of dependencies that can be used for dependency injection. /// A series of key value pairs that dictate runtime execution. @@ -72,8 +73,7 @@ public string DiskFilter { get { - // and 256G - return this.Parameters.GetValue(nameof(this.DiskFilter), "osdisk:false&sizegreaterthan:256g"); + return this.Parameters.GetValue(nameof(this.DiskFilter), "Logical"); } } @@ -136,7 +136,6 @@ public string SharedMemoryBuffer /// A token that can be used to cancel the operation. protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) { - ProcessManager manager = this.SystemManager.ProcessManager; string stateId = $"{nameof(PostgreSQLServerConfiguration)}-{this.Action}-action-success"; ConfigurationState configurationState = await this.stateManager.GetStateAsync(stateId, cancellationToken) .ConfigureAwait(false); @@ -163,10 +162,6 @@ await this.ConfigurePostgreSQLServerAsync(telemetryContext, cancellationToken) await this.SetupPostgreSQLDatabaseAsync(telemetryContext, cancellationToken) .ConfigureAwait(false); break; - case ConfigurationAction.DistributeDatabase: - await this.DistributePostgreSQLDatabaseAsync(telemetryContext, cancellationToken) - .ConfigureAwait(false); - break; } await this.stateManager.SaveStateAsync(stateId, new ConfigurationState(this.Action), cancellationToken); @@ -180,7 +175,9 @@ private async Task ConfigurePostgreSQLServerAsync(EventContext telemetryContext, .FirstOrDefault()?.IPAddress ?? IPAddress.Loopback.ToString(); - string arguments = $"{this.packageDirectory}/configure-server.py --dbName {this.DatabaseName} --serverIp {serverIp} --password {this.SuperUserPassword} --port {this.Port} --inMemory {this.SharedMemoryBuffer}"; + string directory = await this.GetPostgreSQLDataDirectoryAsync(cancellationToken); + + string arguments = $"{this.packageDirectory}/configure-server.py --dbName {this.DatabaseName} --serverIp {serverIp} --password {this.SuperUserPassword} --port {this.Port} --inMemory {this.SharedMemoryBuffer} --directory {directory}"; using (IProcessProxy process = await this.ExecuteCommandAsync( "python3", @@ -197,88 +194,108 @@ private async Task ConfigurePostgreSQLServerAsync(EventContext telemetryContext, } } - private async Task SetupPostgreSQLDatabaseAsync(EventContext telemetryContext, CancellationToken cancellationToken) + private async Task GetPostgreSQLDataDirectoryAsync(CancellationToken cancellationToken) { - string arguments = $"{this.packageDirectory}/setup-database.py --dbName {this.DatabaseName} --password {this.SuperUserPassword} --port {this.Port}"; + IEnumerable disks = await this.SystemManager.DiskManager.GetDisksAsync(cancellationToken) + .ConfigureAwait(false); - using (IProcessProxy process = await this.ExecuteCommandAsync( - "python3", - arguments, - this.packageDirectory, - telemetryContext, - cancellationToken)) + IEnumerable filteredDisks = DiskFilters.FilterDisks(disks, this.DiskFilter, this.Platform); + + string accessPath = filteredDisks + .SelectMany(d => d.Volumes) + .SelectMany(v => v.AccessPaths) + .FirstOrDefault(); + + // lshw typically does not report LVM logical volumes (/dev/dm-*, /dev/mapper/*). + // Check /proc/mounts directly for any LVM-backed mount point as a fallback. + if (string.IsNullOrEmpty(accessPath) && this.Platform != PlatformID.Win32NT) { - if (!cancellationToken.IsCancellationRequested) + accessPath = await this.FindLvmMountPathAsync(cancellationToken).ConfigureAwait(false); + } + + // No logical volume found — fall back to the biggest non-OS physical disk. + if (string.IsNullOrEmpty(accessPath)) + { + const string physicalDiskFilter = "OsDisk:false&BiggestSize"; + IEnumerable physicalDisks = DiskFilters.FilterDisks(disks, physicalDiskFilter, this.Platform); + + try { - await this.LogProcessDetailsAsync(process, telemetryContext, "PostgreSQLServerConfiguration", logToFile: true); - process.ThrowIfDependencyInstallationFailed(process.StandardError.ToString()); + accessPath = physicalDisks.FirstOrDefault()?.GetPreferredAccessPath(this.Platform); + } + catch (Exception) + { + // The disk may not have any eligible volumes. } } - } - private async Task DistributePostgreSQLDatabaseAsync(EventContext telemetryContext, CancellationToken cancellationToken) - { - string innoDbDirs = await this.GetPostgreSQLInnodbDirectoriesAsync(cancellationToken); + if (string.IsNullOrEmpty(accessPath)) + { + throw new DependencyException( + "Expected disks not found. Given the parameters defined for the profile action/step or those passed " + + "in on the command line, the requisite disks do not exist on the system or could not be identified based on the properties " + + "of the existing disks.", + ErrorReason.DependencyNotFound); + } - string arguments = $"{this.packageDirectory}/distribute-database.py --dbName {this.DatabaseName} --directories \"{innoDbDirs}\" --password {this.SuperUserPassword}"; + string directory = this.Combine(accessPath, "postgresql"); - using (IProcessProxy process = await this.ExecuteCommandAsync( - PythonCommand, - arguments, - Environment.CurrentDirectory, - telemetryContext, - cancellationToken)) + if (!this.SystemManager.FileSystem.Directory.Exists(directory)) { - if (!cancellationToken.IsCancellationRequested) - { - await this.LogProcessDetailsAsync(process, telemetryContext, "PostgreSQLServerConfiguration", logToFile: true); - process.ThrowIfDependencyInstallationFailed(process.StandardError.ToString()); - } + this.SystemManager.FileSystem.Directory.CreateDirectory(directory); } + + return directory; } - private async Task GetPostgreSQLInnodbDirectoriesAsync(CancellationToken cancellationToken) + /// + /// Reads /proc/mounts to find a mount point backed by an LVM device (/dev/mapper/* or /dev/dm-*). + /// Returns the mount path (e.g. /home/user/mnt_raid0) or null if none is found. + /// + private async Task FindLvmMountPathAsync(CancellationToken cancellationToken) { - string diskPaths = string.Empty; - - if (!cancellationToken.IsCancellationRequested) + try { - IEnumerable disks = await this.SystemManager.DiskManager.GetDisksAsync(cancellationToken) - .ConfigureAwait(false); + string procMounts = await this.SystemManager.FileSystem.File.ReadAllTextAsync("/proc/mounts", cancellationToken) + .ConfigureAwait(false); - if (disks?.Any() != true) + foreach (string line in procMounts.Split('\n', StringSplitOptions.RemoveEmptyEntries)) { - throw new WorkloadException( - "Unexpected scenario. The disks defined for the system could not be properly enumerated.", - ErrorReason.WorkloadUnexpectedAnomaly); + string[] parts = line.Split(' '); + if (parts.Length >= 2 + && (parts[0].StartsWith("/dev/mapper/", StringComparison.OrdinalIgnoreCase) + || parts[0].StartsWith("/dev/dm-", StringComparison.OrdinalIgnoreCase))) + { + this.Logger.LogTraceMessage($"{this.TypeName}: Found LVM mount from /proc/mounts: device='{parts[0]}', path='{parts[1]}'."); + return parts[1]; + } } + } + catch (Exception ex) + { + this.Logger.LogTraceMessage($"{this.TypeName}: Could not read /proc/mounts: {ex.Message}"); + } - IEnumerable disksToTest = DiskFilters.FilterDisks(disks, this.DiskFilter, this.Platform).ToList(); + return null; + } - if (disksToTest?.Any() != true) - { - throw new WorkloadException( - "Expected disks to test not found. Given the parameters defined for the profile action/step or those passed " + - "in on the command line, the requisite disks do not exist on the system or could not be identified based on the properties " + - "of the existing disks.", - ErrorReason.DependencyNotFound); - } + private async Task SetupPostgreSQLDatabaseAsync(EventContext telemetryContext, CancellationToken cancellationToken) + { + string arguments = $"{this.packageDirectory}/setup-database.py --dbName {this.DatabaseName} --password {this.SuperUserPassword} --port {this.Port}"; - foreach (Disk disk in disksToTest) + using (IProcessProxy process = await this.ExecuteCommandAsync( + "python3", + arguments, + this.packageDirectory, + telemetryContext, + cancellationToken)) + { + if (!cancellationToken.IsCancellationRequested) { - string postgresqlPath = this.Combine(disk.GetPreferredAccessPath(this.Platform), "postgresql"); - - // Create the directory if it doesn't exist - if (!this.SystemManager.FileSystem.Directory.Exists(postgresqlPath)) - { - this.SystemManager.FileSystem.Directory.CreateDirectory(postgresqlPath); - } - - diskPaths += $"{postgresqlPath};"; + await this.LogProcessDetailsAsync(process, telemetryContext, "PostgreSQLServerConfiguration", logToFile: true); + process.ThrowIfDependencyInstallationFailed(process.StandardError.ToString()); } } - - return diskPaths; } /// @@ -295,12 +312,6 @@ internal class ConfigurationAction /// Creates Database on PostgreSQL server and Users on Server and any Clients. /// public const string SetupDatabase = nameof(SetupDatabase); - - /// - /// Distributes existing database to disks on the system - /// - public const string DistributeDatabase = nameof(DistributeDatabase); - } internal class ConfigurationState diff --git a/src/VirtualClient/VirtualClient.Dependencies/StripeDisks.cs b/src/VirtualClient/VirtualClient.Dependencies/StripeDisks.cs index c2d4108ec3..f5fb351dd6 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/StripeDisks.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/StripeDisks.cs @@ -137,6 +137,20 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can /// protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) { + // If the mount directory already exists and has content (e.g. from a previous run), + // skip the striping operation to avoid destroying existing data. + if (!string.IsNullOrEmpty(this.MountDirectory) + && this.fileSystem.Directory.Exists(this.MountDirectory) + && this.fileSystem.Directory.EnumerateFileSystemEntries(this.MountDirectory).Any()) + { + this.Logger.LogTraceMessage($"{this.TypeName}: Skipping. Mount directory '{this.MountDirectory}' already exists and contains data."); + telemetryContext.AddContext("skipped", true); + telemetryContext.AddContext("mountDirectory", this.MountDirectory); + return; + } + + // Discover all disks on the system and apply the user-specified filter + // (e.g. "osdisk:false&sizegreaterthan:256g") to identify eligible disks. IEnumerable allDisks = await this.systemManagement.DiskManager.GetDisksAsync(cancellationToken); IEnumerable filteredDisks = DiskFilters.FilterDisks(allDisks, this.DiskFilter, this.Platform); @@ -147,6 +161,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel ErrorReason.DependencyNotFound); } + // When DiskCount is specified, limit to that many disks (largest first). if (this.DiskCount > 0) { if (filteredDisks.Count() < this.DiskCount) @@ -162,11 +177,15 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel .Take(this.DiskCount); } + // Build the comma-separated list of device paths (e.g. "/dev/sdc,/dev/sdd") + // to pass to the platform-specific striping script. string diskPaths = string.Join(",", filteredDisks.Select(d => d.DevicePath)); string command; string commandArguments; + // On Windows the script is a .cmd batch file; on Linux it is a bash script + // that also receives the resolved mount directory for the striped volume. if (this.Platform == PlatformID.Win32NT) { command = "cmd"; @@ -182,6 +201,8 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel .AddContext("command", command) .AddContext("commandArguments", commandArguments); + // Execute the script with elevated privileges. The script handles partitioning, + // LVM striping (for multiple disks), filesystem creation, and mounting. using (IProcessProxy process = await this.ExecuteCommandAsync(command, commandArguments, this.ScriptDirectory, telemetryContext, cancellationToken, runElevated: true)) { if (!cancellationToken.IsCancellationRequested) @@ -193,7 +214,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel } /// - /// Resolves the mount directory path using the same logic as . + /// Resolves the mount directory path using the same convention as . /// Uses if provided, otherwise determines the path based on /// the current platform and logged-in user. /// diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-OLTP.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-OLTP.json index 4ee1f21ca3..e96bb72fbd 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-OLTP.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-OLTP.json @@ -151,16 +151,29 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", + "Parameters": { + "Scenario": "InstallLinuxPackages", + "Packages": "python3" + } + }, + { + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "MountDisks", + "Type": "StripeDisks", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", "Role": "Server" } }, @@ -169,7 +182,7 @@ "Parameters": { "Scenario": "DownloadMySqlServerPackage", "BlobContainer": "packages", - "BlobName": "mysql.8.0.36.rev3.zip", + "BlobName": "mysql-server-8.0.36-v5.zip", "PackageName": "mysql-server", "Extract": true, "Role": "Server" @@ -180,18 +193,11 @@ "Parameters": { "Scenario": "DownloadSysbenchPackage", "BlobContainer": "packages", - "BlobName": "sysbench-1.0.20.rev3.zip", + "BlobName": "sysbench-1.0.20.rev5.zip", "PackageName": "sysbench", "Extract": true } }, - { - "Type": "LinuxPackageInstallation", - "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" - } - }, { "Type": "MySQLServerInstallation", "Parameters": { @@ -208,7 +214,7 @@ "Scenario": "ConfigureMySQLServer", "Action": "ConfigureServer", "Benchmark": "OLTP", - "DiskFilter": "$.Parameters.DiskFilter", + "DiskFilter": "Logical", "PackageName": "mysql-server", "Role": "Server" } @@ -230,37 +236,13 @@ "Scenario": "SetMySQLGlobalVariables", "Action": "SetGlobalVariables", "Benchmark": "OLTP", - "DiskFilter": "$.Parameters.DiskFilter", + "DiskFilter": "Logical", "InnodbBufferPoolSize": "$.Parameters.InnodbBufferPoolSize", "Variables": "MAX_PREPARED_STMT_COUNT=1000000;MAX_CONNECTIONS=100000;innodb_buffer_pool_size={InnodbBufferPoolSize};innodb_lock_wait_timeout=300;innodb_io_capacity=10000;innodb_io_capacity_max=10000;innodb_buffer_pool_dump_at_shutdown=OFF;innodb_change_buffering=0;table_open_cache=20000;", "PackageName": "mysql-server", "Role": "Server" } }, - { - "Type": "SysbenchConfiguration", - "Parameters": { - "Scenario": "PrepareMySQLDatabase", - "Action": "CreateTables", - "DatabaseSystem": "MySQL", - "Benchmark": "OLTP", - "DatabaseName": "$.Parameters.DatabaseName", - "DatabaseScenario": "$.Parameters.DatabaseScenario", - "PackageName": "sysbench", - "Role": "Server" - } - }, - { - "Type": "MySQLServerConfiguration", - "Parameters": { - "Scenario": "DistributeMySQLDatabase", - "Action": "DistributeDatabase", - "DiskFilter": "$.Parameters.DiskFilter", - "DatabaseName": "$.Parameters.DatabaseName", - "PackageName": "mysql-server", - "Role": "Server" - } - }, { "Type": "SysbenchConfiguration", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-TPCC.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-TPCC.json index f48802a6e2..f8f7077f13 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-TPCC.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-MYSQL-SYSBENCH-TPCC.json @@ -39,16 +39,29 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", + "Parameters": { + "Scenario": "InstallLinuxPackages", + "Packages": "python3" + } + }, + { + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "MountDisks", + "Type": "StripeDisks", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", "Role": "Server" } }, @@ -57,7 +70,7 @@ "Parameters": { "Scenario": "DownloadMySqlServerPackage", "BlobContainer": "packages", - "BlobName": "mysql.8.0.36.rev3.zip", + "BlobName": "mysql-server-8.0.36-v5.zip", "PackageName": "mysql-server", "Extract": true, "Role": "Server" @@ -68,18 +81,11 @@ "Parameters": { "Scenario": "DownloadSysbenchPackage", "BlobContainer": "packages", - "BlobName": "sysbench-1.0.20.rev3.zip", + "BlobName": "sysbench-1.0.20.rev5.zip", "PackageName": "sysbench", "Extract": true } }, - { - "Type": "LinuxPackageInstallation", - "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" - } - }, { "Type": "MySQLServerInstallation", "Parameters": { @@ -96,7 +102,7 @@ "Scenario": "ConfigureMySQLServer", "Action": "ConfigureServer", "Benchmark": "TPCC", - "DiskFilter": "$.Parameters.DiskFilter", + "DiskFilter": "Logical", "PackageName": "mysql-server", "Role": "Server" } @@ -118,37 +124,13 @@ "Scenario": "SetMySQLGlobalVariables", "Action": "SetGlobalVariables", "Benchmark": "TPCC", - "DiskFilter": "$.Parameters.DiskFilter", + "DiskFilter": "Logical", "InnodbBufferPoolSize": "$.Parameters.InnodbBufferPoolSize", "Variables": "MAX_PREPARED_STMT_COUNT=1000000;MAX_CONNECTIONS=100000;innodb_buffer_pool_size={InnodbBufferPoolSize};innodb_lock_wait_timeout=300;innodb_io_capacity=10000;innodb_io_capacity_max=10000;innodb_buffer_pool_dump_at_shutdown=OFF;innodb_change_buffering=0;table_open_cache=20000;", "PackageName": "mysql-server", "Role": "Server" } }, - { - "Type": "SysbenchConfiguration", - "Parameters": { - "Scenario": "PrepareMySQLDatabase", - "Action": "CreateTables", - "DatabaseSystem": "MySQL", - "Benchmark": "TPCC", - "DatabaseName": "$.Parameters.DatabaseName", - "DatabaseScenario": "$.Parameters.DatabaseScenario", - "PackageName": "sysbench", - "Role": "Server" - } - }, - { - "Type": "MySQLServerConfiguration", - "Parameters": { - "Scenario": "DistributeMySQLDatabase", - "Action": "DistributeDatabase", - "DatabaseName": "$.Parameters.DatabaseName", - "DiskFilter": "$.Parameters.DiskFilter", - "PackageName": "mysql-server", - "Role": "Server" - } - }, { "Type": "SysbenchConfiguration", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCC.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCC.json index 51666405d7..832be21a0a 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCC.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCC.json @@ -1,5 +1,5 @@ { - "Description": "HammerDB PostgrSQL TPCC Database Server Performance Workload", + "Description": "HammerDB PostgreSQL TPCC Database Server Performance Workload", "MinimumExecutionInterval": "00:01:00", "Metadata": { "RecommendedMinimumExecutionTime": "04:00:00", @@ -12,7 +12,7 @@ "Port": "5432", "Duration": "00:20:00", "VirtualUsers": "{calculate({LogicalCoreCount})}", - "WarehouseCount": "{calculate({SystemMemoryMegabytes} * 15 / 800)}", + "WarehouseCount": "{calculate({LogicalCoreCount} * 10)}", "SharedMemoryBuffer": "{calculate({SystemMemoryMegabytes} * 85 / 100)}" }, "Actions": [ @@ -49,24 +49,30 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", - "Role": "Server" + "Scenario": "InstallLinuxPackages", + "Packages": "python3" } }, { - "Type": "MountDisks", + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "LinuxPackageInstallation", + "Type": "StripeDisks", "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", + "Role": "Server" } }, { @@ -74,7 +80,7 @@ "Parameters": { "Scenario": "DownloadPostgreSQLPackage", "BlobContainer": "packages", - "BlobName": "postgresql.14.0.0.rev3.zip", + "BlobName": "postgresql.14.0.0.rev4.zip", "PackageName": "postgresql", "Extract": true } @@ -84,7 +90,7 @@ "Parameters": { "Scenario": "DownloadHammerDBPackage", "BlobContainer": "packages", - "BlobName": "hammerdb.4.12.0.rev3.zip", + "BlobName": "hammerdb.4.12.0.rev4.zip", "PackageName": "hammerdb", "Extract": true } diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCH.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCH.json index da5ec3bde5..82bbd23175 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCH.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-HAMMERDB-TPCH.json @@ -12,7 +12,7 @@ "Port": "5432", "Duration": "00:20:00", "VirtualUsers": "{calculate({LogicalCoreCount})}", - "ScaleFactor": "10", + "ScaleFactor": "1", "SharedMemoryBuffer": "{calculate({SystemMemoryMegabytes} * 85 / 100)}" }, "Actions": [ @@ -49,24 +49,30 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", - "Role": "Server" + "Scenario": "InstallLinuxPackages", + "Packages": "python3" } }, { - "Type": "MountDisks", + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "LinuxPackageInstallation", + "Type": "StripeDisks", "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", + "Role": "Server" } }, { @@ -74,7 +80,7 @@ "Parameters": { "Scenario": "DownloadPostgreSQLPackage", "BlobContainer": "packages", - "BlobName": "postgresql.14.0.0.rev3.zip", + "BlobName": "postgresql.14.0.0.rev4.zip", "PackageName": "postgresql", "Extract": true } @@ -84,7 +90,7 @@ "Parameters": { "Scenario": "DownloadHammerDBPackage", "BlobContainer": "packages", - "BlobName": "hammerdb.4.12.0.rev3.zip", + "BlobName": "hammerdb.4.12.0.rev4.zip", "PackageName": "hammerdb", "Extract": true } diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-OLTP.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-OLTP.json index f23278cc60..8fd94e04e5 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-OLTP.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-OLTP.json @@ -152,16 +152,29 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", + "Parameters": { + "Scenario": "InstallLinuxPackages", + "Packages": "python3" + } + }, + { + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "MountDisks", + "Type": "StripeDisks", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", "Role": "Server" } }, @@ -170,7 +183,7 @@ "Parameters": { "Scenario": "DownloadPostgreSQLServerPackage", "BlobContainer": "packages", - "BlobName": "postgresql.14.0.0.rev3.zip", + "BlobName": "postgresql.14.0.0.rev4.zip", "PackageName": "postgresql", "Extract": true, "Role": "Server" @@ -181,18 +194,11 @@ "Parameters": { "Scenario": "DownloadSysbenchPackage", "BlobContainer": "packages", - "BlobName": "sysbench-1.0.20.rev3.zip", + "BlobName": "sysbench-1.0.20.rev5.zip", "PackageName": "sysbench", "Extract": true } }, - { - "Type": "LinuxPackageInstallation", - "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" - } - }, { "Type": "PostgreSQLServerInstallation", "Parameters": { @@ -226,31 +232,6 @@ "SharedMemoryBuffer": "$.Parameters.SharedMemoryBuffer" } }, - { - "Type": "SysbenchConfiguration", - "Parameters": { - "Scenario": "PreparePostgreSQLDatabase", - "Action": "CreateTables", - "DatabaseSystem": "PostgreSQL", - "Benchmark": "OLTP", - "DatabaseName": "$.Parameters.DatabaseName", - "DatabaseScenario": "$.Parameters.DatabaseScenario", - "PackageName": "sysbench", - "Role": "Server" - } - }, - { - "Type": "PostgreSQLServerConfiguration", - "Parameters": { - "Scenario": "DistributePostgreSQLDatabase", - "Action": "DistributeDatabase", - "DatabaseName": "$.Parameters.DatabaseName", - "DiskFilter": "$.Parameters.DiskFilter", - "PackageName": "postgresql", - "Port": "$.Parameters.Port", - "Role": "Server" - } - }, { "Type": "SysbenchConfiguration", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-TPCC.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-TPCC.json index 093c760126..8b7ac7a628 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-TPCC.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-POSTGRESQL-SYSBENCH-TPCC.json @@ -1,5 +1,5 @@ { - "Description": "Sysbench TPCC MySQL Database Server Performance Workload", + "Description": "Sysbench TPCC PostgreSQL Database Server Performance Workload", "MinimumExecutionInterval": "00:01:00", "Metadata": { "RecommendedMinimumExecutionTime": "04:00:00", @@ -40,16 +40,29 @@ ], "Dependencies": [ { - "Type": "FormatDisks", + "Type": "LinuxPackageInstallation", + "Parameters": { + "Scenario": "InstallLinuxPackages", + "Packages": "python3" + } + }, + { + "Type": "DependencyPackageInstallation", "Parameters": { - "Scenario": "FormatDisks", + "Scenario": "DownloadSystemConfigPackage", + "BlobContainer": "packages", + "BlobName": "system_config.1.1.0.zip", + "PackageName": "system_config", + "Extract": true, "Role": "Server" } }, { - "Type": "MountDisks", + "Type": "StripeDisks", "Parameters": { - "Scenario": "CreateMountPoints", + "Scenario": "StripeAndMountDisks", + "PackageName": "system_config", + "DiskFilter": "$.Parameters.DiskFilter", "Role": "Server" } }, @@ -58,7 +71,7 @@ "Parameters": { "Scenario": "DownloadPostgreSQLServerPackage", "BlobContainer": "packages", - "BlobName": "postgresql.14.0.0.rev3.zip", + "BlobName": "postgresql.14.0.0.rev4.zip", "PackageName": "postgresql", "Extract": true, "Role": "Server" @@ -69,18 +82,11 @@ "Parameters": { "Scenario": "DownloadSysbenchPackage", "BlobContainer": "packages", - "BlobName": "sysbench-1.0.20.rev3.zip", + "BlobName": "sysbench-1.0.20.rev5.zip", "PackageName": "sysbench", "Extract": true } }, - { - "Type": "LinuxPackageInstallation", - "Parameters": { - "Scenario": "InstallLinuxPackages", - "Packages": "python3" - } - }, { "Type": "PostgreSQLServerInstallation", "Parameters": { @@ -114,31 +120,6 @@ "SharedMemoryBuffer": "$.Parameters.SharedMemoryBuffer" } }, - { - "Type": "SysbenchConfiguration", - "Parameters": { - "Scenario": "PreparePostgreSQLDatabase", - "Action": "CreateTables", - "DatabaseSystem": "PostgreSQL", - "Benchmark": "TPCC", - "DatabaseName": "$.Parameters.DatabaseName", - "DatabaseScenario": "$.Parameters.DatabaseScenario", - "PackageName": "sysbench", - "Role": "Server" - } - }, - { - "Type": "PostgreSQLServerConfiguration", - "Parameters": { - "Scenario": "DistributePostgreSQLDatabase", - "Action": "DistributeDatabase", - "DatabaseName": "$.Parameters.DatabaseName", - "DiskFilter": "$.Parameters.DiskFilter", - "PackageName": "postgresql", - "Port": "$.Parameters.Port", - "Role": "Server" - } - }, { "Type": "SysbenchConfiguration", "Parameters": {