Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieving Disk Paths via DiskManager #440

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -216,39 +216,6 @@ public void FioExecutorAppliesConfigurationParametersCorrectly_DiskFillScenario(
}
}

[Test]
public async Task FioExecutorCreatesExpectedMountPointsForDisksUnderTest_RemoteDiskScenario()
{
// Clear any access points out.
this.disks.ToList().ForEach(disk => disk.Volumes.ToList().ForEach(vol => (vol.AccessPaths as List<string>).Clear()));

List<Tuple<DiskVolume, string>> mountPointsCreated = new List<Tuple<DiskVolume, string>>();

this.DiskManager
.Setup(mgr => mgr.CreateMountPointAsync(It.IsAny<DiskVolume>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.Callback<DiskVolume, string, CancellationToken>((volume, mountPoint, token) =>
{
(volume.AccessPaths as List<string>).Add(mountPoint);
})
.Returns(Task.CompletedTask);

using (TestFioExecutor workloadExecutor = new TestFioExecutor(this.Dependencies, this.profileParameters))
{
await workloadExecutor.ExecuteAsync(CancellationToken.None);

Assert.IsTrue(this.disks.Skip(1).All(d => d.Volumes.First().AccessPaths?.Any() == true));

string expectedMountPoint1 = Path.Combine(MockFixture.TestAssemblyDirectory, "vcmnt_dev_sdd1");
Assert.AreEqual(expectedMountPoint1, this.disks.ElementAt(1).Volumes.First().AccessPaths.First());

string expectedMountPoint2 = Path.Combine(MockFixture.TestAssemblyDirectory, "vcmnt_dev_sde1");
Assert.AreEqual(expectedMountPoint2, this.disks.ElementAt(2).Volumes.First().AccessPaths.First());

string expectedMountPoint3 = Path.Combine(MockFixture.TestAssemblyDirectory, "vcmnt_dev_sdf1");
Assert.AreEqual(expectedMountPoint3, this.disks.ElementAt(3).Volumes.First().AccessPaths.First());
}
}

[Test]
public void FioExecutorCreatesTheExpectedWorkloadProcess_Scenario1()
{
Expand Down
70 changes: 4 additions & 66 deletions src/VirtualClient/VirtualClient.Actions/FIO/FioExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel

string ioEngine = FioExecutor.GetIOEngine(Environment.OSVersion.Platform);

IEnumerable<Disk> disks = await this.SystemManagement.DiskManager.GetDisksAsync(cancellationToken)
this.DiskFilter = string.IsNullOrWhiteSpace(this.DiskFilter) ? DiskFilters.DefaultDiskFilter : this.DiskFilter;

IEnumerable<Disk> disks = await this.SystemManagement.DiskManager.GetFilteredDisksAsync(this.Platform, this.DiskFilter, cancellationToken)
.ConfigureAwait(false);

if (disks?.Any() != true)
Expand All @@ -313,37 +315,15 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel
ErrorReason.WorkloadUnexpectedAnomaly);
}

IEnumerable<Disk> disksToTest = this.GetDisksToTest(disks);

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);
}

if (await this.CreateMountPointsAsync(disksToTest, cancellationToken).ConfigureAwait(false))
{
// Refresh the disks to pickup the mount point changes.
await Task.Delay(1000).ConfigureAwait(false);
IEnumerable<Disk> updatedDisks = await this.SystemManagement.DiskManager.GetDisksAsync(cancellationToken)
.ConfigureAwait(false);

disksToTest = this.GetDisksToTest(updatedDisks);
}

telemetryContext.AddContext(nameof(this.DiskFilter), this.DiskFilter);
telemetryContext.AddContext("executable", this.ExecutablePath);
telemetryContext.AddContext(nameof(ioEngine), ioEngine);
telemetryContext.AddContext(nameof(disks), disks);
telemetryContext.AddContext(nameof(disksToTest), disksToTest);

this.WorkloadProcesses.Clear();
List<Task> fioProcessTasks = new List<Task>();

this.WorkloadProcesses.AddRange(this.CreateWorkloadProcesses(this.ExecutablePath, this.CommandLine, disksToTest, this.ProcessModel));
this.WorkloadProcesses.AddRange(this.CreateWorkloadProcesses(this.ExecutablePath, this.CommandLine, disks, this.ProcessModel));

using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
{
Expand Down Expand Up @@ -386,48 +366,6 @@ await this.DeleteTestFilesAsync(workload.TestFiles)
}
}

/// <summary>
/// Creates mount points for any disks that do not have them already.
/// </summary>
/// <param name="disks">This disks on which to create the mount points.</param>
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param>
protected async Task<bool> CreateMountPointsAsync(IEnumerable<Disk> disks, CancellationToken cancellationToken)
{
bool mountPointsCreated = false;

// Don't mount any partition in OS drive.
foreach (Disk disk in disks.Where(d => !d.IsOperatingSystem()))
{
// mount every volume that doesn't have an accessPath.
foreach (DiskVolume volume in disk.Volumes.Where(v => v.AccessPaths?.Any() != true))
{
string newMountPoint = volume.GetDefaultMountPoint();
this.Logger.LogTraceMessage($"Create Mount Point: {newMountPoint}");

EventContext relatedContext = EventContext.Persisted().Clone()
.AddContext(nameof(volume), volume)
.AddContext("mountPoint", newMountPoint);

await this.Logger.LogMessageAsync($"{this.TypeName}.CreateMountPoint", relatedContext, async () =>
{
string newMountPoint = volume.GetDefaultMountPoint();
if (!this.SystemManagement.FileSystem.Directory.Exists(newMountPoint))
{
this.SystemManagement.FileSystem.Directory.CreateDirectory(newMountPoint).Create();
}

await this.SystemManagement.DiskManager.CreateMountPointAsync(volume, newMountPoint, cancellationToken)
.ConfigureAwait(false);

mountPointsCreated = true;

}).ConfigureAwait(false);
}
}

return mountPointsCreated;
}

/// <summary>
/// Creates a process to run FIO targeting the disks specified.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,20 @@ public void UnixDiskManagerAppliesRetriesOnFailedAttemptsToCreateMountPoints()
Assert.IsTrue(attempts == 4);
}

[Test]
public async Task UnixDiskManagerReturnsListofFilteredDisks()
{
this.testProcess.OnHasExited = () => true;
this.testProcess.OnStart = () => true;
this.testProcess.StandardOutput.Append(Resources.lshw_disk_storage_results);


IEnumerable<Disk> disks = await this.diskManager.GetFilteredDisksAsync(PlatformID.Unix, "osdisk:false", CancellationToken.None)
.ConfigureAwait(false);

Assert.AreEqual(disks.Count(), 3);
}

private class TestUnixDiskManager : UnixDiskManager
{
public TestUnixDiskManager(ProcessManager processManager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,89 @@ public async Task WindowsDiskManagerGetsTheExpectedDisks_Scenario1()
actualDisks.ToList().ForEach(disk => Assert.IsTrue(disk.Volumes.Count() == 2));
}

[Test]
public async Task WindowsDiskManagerReturnsListofFilteredDisks()
{
this.testProcess.OnHasExited = () => true;
this.testProcess.OnStart = () => true;

this.standardInput.BytesWritten += (sender, data) =>
{
string input = data.ToString().Trim();
if (input.Contains($"select disk"))
{
int diskIndex = int.Parse(Regex.Match(input, "[0-9]+").Value);
this.testProcess.StandardOutput.Append($"Disk {diskIndex} is now the selected disk.");
}
else if (input.Contains($"select partition"))
{
int partitionIndex = int.Parse(Regex.Match(input, "[0-9]+").Value);
this.testProcess.StandardOutput.Append($"Partition {partitionIndex} is now the selected partition.");
}
else if (input.Contains("list disk"))
{
StringBuilder listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 1024 KB ");

this.testProcess.StandardOutput.Append(listDiskResults.ToString());
}
else if (input.Contains("list partition"))
{
StringBuilder listPartitionResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Partition ### Type Size Offset")
.AppendLine(" ------------- ---------------- ------- -------")
.AppendLine(" Partition 1 Primary 500 MB 1024 KB")
.AppendLine(" Partition 2 Primary 126 GB 501 MB");

this.testProcess.StandardOutput.Append(listPartitionResults.ToString());
}
else if (input.Contains($"detail disk"))
{
StringBuilder detailDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine("Virtual HD ATA Device")
.AppendLine("Disk ID: EF349D83")
.AppendLine("Type : ATA")
.AppendLine("Status : Online")
.AppendLine("Path : 0")
.AppendLine("Target : 0")
.AppendLine("LUN ID : 0")
.AppendLine(" ")
.AppendLine(" Volume ### Ltr Label Fs Type Size Status Info ")
.AppendLine(" ---------- --- ----------- ----- ---------- ------- --------- --------")
.AppendLine(" Volume 0 System Rese NTFS Partition 500 MB Healthy System ")
.AppendLine(" Volume 1 C Windows NTFS Partition 126 GB Healthy Boot ");

this.testProcess.StandardOutput.Append(detailDiskResults.ToString());
}
else if (input.Contains($"detail partition"))
{
StringBuilder detailPartitionResults = new StringBuilder()
.AppendLine(" ")
.AppendLine("Partition 1")
.AppendLine("Type : 07")
.AppendLine("Hidden: No")
.AppendLine("Active: Yes")
.AppendLine("Offset in Bytes: 525336576")
.AppendLine(" ")
.AppendLine(" Volume ### Ltr Label Fs Type Size Status Info ")
.AppendLine(" ---------- --- ----------- ----- ---------- ------- --------- --------")
.AppendLine(" Volume 1 C Windows NTFS Partition 126 GB Healthy Boot ");

this.testProcess.StandardOutput.Append(detailPartitionResults.ToString());
}
};

IEnumerable<Disk> disks = await this.diskManager.GetFilteredDisksAsync(PlatformID.Win32NT, DiskFilters.DefaultDiskFilter, CancellationToken.None)
.ConfigureAwait(false);

Assert.AreEqual(disks.Count(), 1);
}

private class TestWindowsDiskManager : WindowsDiskManager
{
public TestWindowsDiskManager(ProcessManager processManager)
Expand Down
42 changes: 42 additions & 0 deletions src/VirtualClient/VirtualClient.Core/DiskManagerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace VirtualClient
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Polly;
using VirtualClient.Common.Extensions;
using VirtualClient.Contracts;

/// <summary>
/// Methods for extending the functionality of the
/// disk manager class, and related classes.
/// </summary>
public static class DiskManagerExtensions
{
/// <summary>
/// Gets a filtered set of physical disks that exist on the system.
/// </summary>
/// <param name="diskManager"></param>
/// <param name="platform"></param>
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param>
/// <param name="diskFilter">The filter to apply to the disks.</param>
public static async Task<IEnumerable<Disk>> GetFilteredDisksAsync(this IDiskManager diskManager, PlatformID platform, string diskFilter, CancellationToken cancellationToken)
{
IEnumerable<Disk> disks = await diskManager.GetDisksAsync(cancellationToken)
.ConfigureAwait(false);

IEnumerable<Disk> filteredDisks = DiskFilters.FilterDisks(disks, diskFilter, platform).ToList();

return filteredDisks;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,12 @@
"Scenario": "InitializeDisks"
}
},
{
"Type": "MountDisks",
"Parameters": {
"Scenario": "CreateMountPoints"
}
},
{
"Type": "DependencyPackageInstallation",
"Parameters": {
Expand Down
Loading