Skip to content

Commit e848be6

Browse files
author
Ankit Sharma
committed
added functional tests, updated profile and added documentation for diskspd on raw disk scenario.
1 parent 8422102 commit e848be6

3 files changed

Lines changed: 170 additions & 2 deletions

File tree

src/VirtualClient/VirtualClient.Actions.FunctionalTests/DiskSpdProfileTests.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,102 @@ public void DiskSpdWorkloadProfileActionsWillNotBeExecutedIfTheWorkloadPackageDo
117117
}
118118
}
119119

120+
[Test]
121+
[TestCase("PERF-IO-DISKSPD-RAWDISK.json")]
122+
public void DiskSpdRawDiskWorkloadProfileParametersAreInlinedCorrectly(string profile)
123+
{
124+
this.mockFixture.Setup(PlatformID.Win32NT);
125+
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
126+
{
127+
WorkloadAssert.ParameterReferencesInlined(executor.Profile);
128+
}
129+
}
130+
131+
[Test]
132+
[TestCase("PERF-IO-DISKSPD-RAWDISK.json")]
133+
public async Task DiskSpdRawDiskWorkloadProfileInstallsTheExpectedDependenciesOnWindowsPlatform(string profile)
134+
{
135+
// Raw disk profiles do not require disk formatting — disks are accessed directly at the raw block level.
136+
this.mockFixture.Setup(PlatformID.Win32NT);
137+
138+
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies, dependenciesOnly: true))
139+
{
140+
await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false);
141+
142+
// Workload dependency package expectations
143+
// The workload dependency package should have been installed at this point.
144+
WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "diskspd");
145+
}
146+
}
147+
148+
[Test]
149+
[TestCase("PERF-IO-DISKSPD-RAWDISK.json")]
150+
public async Task DiskSpdRawDiskWorkloadProfileExecutesTheExpectedWorkloadsOnWindowsPlatform(string profile)
151+
{
152+
IEnumerable<string> expectedCommands = DiskSpdProfileTests.GetDiskSpdRawDiskProfileExpectedCommands();
153+
154+
// Setup the expectations for the workload
155+
// - Workload package is installed and exists.
156+
// - Workload binaries/executables exist on the file system.
157+
// - Raw disk discovery returns 2 HDD disks (indices 6 and 7).
158+
// - The workload generates valid results.
159+
this.mockFixture.Setup(PlatformID.Win32NT);
160+
this.mockFixture.SetupPackage("diskspd", expectedFiles: $@"win-x64\diskspd.exe");
161+
162+
this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
163+
{
164+
IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir);
165+
if (arguments.Contains("Get-PhysicalDisk", StringComparison.OrdinalIgnoreCase))
166+
{
167+
// Simulate discovery of 2 HDD raw disks (indices 6 and 7)
168+
process.StandardOutput.Append("6\r\n7");
169+
}
170+
else if (arguments.Contains("diskspd", StringComparison.OrdinalIgnoreCase))
171+
{
172+
process.StandardOutput.Append(TestDependencies.GetResourceFileContents("Results_DiskSpd.txt"));
173+
}
174+
175+
return process;
176+
};
177+
178+
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
179+
{
180+
await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false);
181+
182+
WorkloadAssert.CommandsExecuted(this.mockFixture, expectedCommands.ToArray());
183+
}
184+
}
185+
186+
[Test]
187+
[TestCase("PERF-IO-DISKSPD-RAWDISK.json")]
188+
public void DiskSpdRawDiskWorkloadProfileActionsWillNotBeExecutedIfWorkloadPackageDoesNotExist(string profile)
189+
{
190+
this.mockFixture.Setup(PlatformID.Win32NT);
191+
192+
// We ensure the workload package does not exist.
193+
this.mockFixture.PackageManager.Clear();
194+
195+
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
196+
{
197+
executor.ExecuteDependencies = false;
198+
199+
DependencyException error = Assert.ThrowsAsync<DependencyException>(() => executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None));
200+
Assert.AreEqual(ErrorReason.WorkloadDependencyMissing, error.Reason);
201+
Assert.IsFalse(this.mockFixture.ProcessManager.Commands.Contains("diskspd.exe"));
202+
}
203+
}
204+
205+
private static IEnumerable<string> GetDiskSpdRawDiskProfileExpectedCommands()
206+
{
207+
return new List<string>
208+
{
209+
// ProcessModel=SingleProcessPerDisk: one diskspd process per discovered raw disk.
210+
// Duration=00:01:00 -> 60 seconds; disks #6 and #7 discovered via Get-PhysicalDisk.
211+
@"-b128K -d60 -o32 -t1 -r -w0 -Sh -L -Rtext #6",
212+
@"-b128K -d60 -o32 -t1 -r -w0 -Sh -L -Rtext #7"
213+
};
214+
}
215+
120216
private static IEnumerable<string> GetDiskSpdStressProfileExpectedCommands()
121217
{
122218
return new List<string>

src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-DISKSPD-RAWDISK.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
},
88
"Parameters": {
99
"Duration": "00:01:00",
10-
"ProcessModel": "SingleProcessPerDisk"
10+
"ProcessModel": "SingleProcessPerDisk",
11+
"CommandLine": "-b128K -d{Duration.TotalSeconds} -o32 -t1 -r -w0 -Sh -L -Rtext",
12+
"RawDiskIndexRange": ""
1113
},
1214
"Actions": [
1315
{
@@ -16,10 +18,11 @@
1618
"Scenario": "RandomRead_128k_BlockSize",
1719
"MetricScenario": "diskspd_rawdisk_randread_128k_d32_th1",
1820
"PackageName": "diskspd",
19-
"CommandLine": "-b128K -d{Duration.TotalSeconds} -o32 -t1 -r -w0 -Sh -L -Rtext",
21+
"CommandLine": "$.Parameters.CommandLine",
2022
"Duration": "$.Parameters.Duration",
2123
"ProcessModel": "$.Parameters.ProcessModel",
2224
"RawDiskTarget": true,
25+
"RawDiskIndexRange": "$.Parameters.RawDiskIndexRange",
2326
"Tags": "IO,DiskSpd,randread,rawdisk"
2427
}
2528
}

website/docs/workloads/diskspd/diskspd-profiles.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,73 @@ aspects of the workload execution.
143143

144144
# Run specific scenarios only. Each action in a profile as a 'Scenario' name.
145145
VirtualClient.exe --profile=PERF-IO-DISKSPD.json --system=Demo --timeout=1440 --scenarios=RandomWrite_4k_BlockSize,RandomWrite_8k_BlockSize,RandomRead_8k_BlockSize,RandomRead_4k_BlockSize
146+
```
147+
148+
## PERF-IO-DISKSPD-RAWDISK.json
149+
Runs a read I/O workload using the DiskSpd toolset targeting raw physical HDD disks directly (no filesystem). This profile is a Windows-only profile
150+
designed for bare-metal or JBOD scenarios where disks are not formatted or mounted. It targets disks at the raw block level using DiskSpd's native `#N`
151+
physical disk index syntax, bypassing the Windows volume manager entirely.
152+
153+
The profile auto-discovers HDD disks at runtime using `Get-PhysicalDisk | Where-Object { $_.MediaType -eq 'HDD' }`, which correctly enumerates offline
154+
JBOD drives that DiskPart/DiskManager cannot see. An explicit `RawDiskIndexRange` parameter can be supplied to override auto-discovery. One DiskSpd
155+
process is launched per discovered disk (`ProcessModel=SingleProcessPerDisk`).
156+
157+
* [Workload Profile](https://github.com/microsoft/VirtualClient/blob/main/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-DISKSPD-RAWDISK.json)
158+
159+
* **Supported Platform/Architectures**
160+
* win-x64
161+
* win-arm64
162+
163+
* **Supported Operating Systems**
164+
* Windows 10 / Windows 11
165+
* Windows Server 2016 / 2019 / 2022
166+
167+
* **Supports Disconnected Scenarios**
168+
* Yes. When the DiskSpd package is included in the 'packages' directory.
169+
170+
* **Dependencies**
171+
* Internet connection (for downloading the DiskSpd package on first run).
172+
* Physical HDD disks present on the system (auto-discovered via `Get-PhysicalDisk`).
173+
174+
* **Scenarios**
175+
* Random Read Operations
176+
* 128k block size, queue depth 32, 1 thread per disk (`RandomRead_128k_BlockSize`)
177+
178+
* **Profile Parameters**
179+
The following parameters can be optionally supplied on the command line to modify the behaviors of the workload.
180+
181+
| Parameter | Purpose | Default Value |
182+
|--------------------|---------|---------------|
183+
| CommandLine | Optional. The DiskSpd command line arguments template. Supports `{Duration.TotalSeconds}` substitution. | `-b128K -d{Duration.TotalSeconds} -o32 -t1 -r -w0 -Sh -L -Rtext` |
184+
| Duration | Optional. The duration of each DiskSpd scenario/action. | 1 minute |
185+
| ProcessModel | Optional. Defines how DiskSpd processes are distributed across disks. `SingleProcessPerDisk` runs one process per raw disk. | SingleProcessPerDisk |
186+
| RawDiskIndexRange | Optional. Overrides auto-discovery when provided. Accepted forms: a hyphen range (e.g. `6-180`), a single index (e.g. `36`), or a comma-separated list (e.g. `37,38,39,40` — see note below). When empty or omitted, HDD disks are auto-discovered via `Get-PhysicalDisk`. | (auto-discovered HDD disks) |
187+
188+
> **Note on comma-separated lists:** The VC CLI uses `",,,"` as the delimiter between multiple `--parameters` values. A comma-separated `RawDiskIndexRange` (e.g. `35,38`) must therefore be followed by `,,,` so the parser treats it as a single value rather than splitting on the commas. The simplest way is to append a trailing `",,,"` (e.g. `"RawDiskIndexRange=35,38,,,"`) or include another parameter (e.g. `"RawDiskIndexRange=37,38,39,40,,,Duration=00:00:30"`). Contiguous ranges via hyphen syntax (e.g. `30-40`) have no such requirement.
189+
190+
* **Usage Examples**
191+
The following section provides a few basic examples of how to use the workload profile.
192+
193+
``` bash
194+
# Run the workload — HDD disks are auto-discovered via Get-PhysicalDisk (MediaType=HDD)
195+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440
196+
197+
# Auto-discover disks with a custom duration (30 seconds per scenario)
198+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="Duration=00:00:30"
199+
200+
# Target a single disk by index
201+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="RawDiskIndexRange=36,,,Duration=00:00:30"
202+
203+
# Target a contiguous range of disks using hyphen syntax (disks 30 through 31)
204+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="RawDiskIndexRange=30-31,,,Duration=00:00:30"
205+
206+
# Target a non-contiguous set of disks using a comma-separated list
207+
# Append a trailing ",,," so the parser treats the value as a single token
208+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="RawDiskIndexRange=35,38,,,"
209+
210+
# Comma-separated list combined with another parameter (trailing ",,," not needed in this case)
211+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="RawDiskIndexRange=37,38,39,40,,,Duration=00:00:30"
212+
213+
# Override the command line (e.g. change block size to 64K)
214+
VirtualClient.exe --profile=PERF-IO-DISKSPD-RAWDISK.json --system=Demo --timeout=1440 --parameters="CommandLine=-b64K -d{Duration.TotalSeconds} -o32 -t1 -r -w0 -Sh -L -Rtext,,,Duration=00:00:30"
146215
```

0 commit comments

Comments
 (0)