⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,193 @@ public void WindowsDiskManagerParsesDiskPartListDiskResultsCorrectly_AzureVMScen
CollectionAssert.AreEquivalent(new List<int> { 0, 1, 2, 3 }, diskIndexes);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesFromListDiskResultsCorrectly_AzureVMScenario()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 1024 KB ")
.AppendLine(" Disk 1 Online 32 GB 0 B ")
.AppendLine(" Disk 2 Online 1024 GB 0 B ")
.AppendLine(" Disk 3 Online 1024 GB 0 B ")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(4, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("32 GB", diskSizes[1]);
Assert.AreEqual("1024 GB", diskSizes[2]);
Assert.AreEqual("1024 GB", diskSizes[3]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesWithMultipleDigitDiskIndices()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 1024 KB ")
.AppendLine(" Disk 10 Online 1024 GB 0 B *")
.AppendLine(" Disk 127 Online 2048 GB 512 GB ")
.AppendLine(" Disk 255 Offline 10 TB 0 B ")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(4, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("1024 GB", diskSizes[10]);
Assert.AreEqual("2048 GB", diskSizes[127]);
Assert.AreEqual("10 TB", diskSizes[255]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesWithVariousSizeUnits()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 1024 KB 0 B ")
.AppendLine(" Disk 1 Online 512 MB 0 B ")
.AppendLine(" Disk 2 Online 256 GB 0 B ")
.AppendLine(" Disk 3 Online 5 TB 0 B ")
.AppendLine(" Disk 4 Online 2 PB 0 B ")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(5, diskSizes.Count);
Assert.AreEqual("1024 KB", diskSizes[0]);
Assert.AreEqual("512 MB", diskSizes[1]);
Assert.AreEqual("256 GB", diskSizes[2]);
Assert.AreEqual("5 TB", diskSizes[3]);
Assert.AreEqual("2 PB", diskSizes[4]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesWithDifferentStatuses()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 0 B ")
.AppendLine(" Disk 1 Offline 1024 GB 0 B ")
.AppendLine(" Disk 2 No Media 0 B 0 B ")
.AppendLine(" Disk 3 Errors 512 GB 0 B ")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(4, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("1024 GB", diskSizes[1]);
Assert.AreEqual("0 B", diskSizes[2]);
Assert.AreEqual("512 GB", diskSizes[3]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesWithVaryingWhitespace()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 1024 KB ") // Extra leading space
.AppendLine(" Disk 1 Online 32 GB 0 B ") // Extra space after index
.AppendLine("Disk 2 Online 1024 GB 0 B ") // No leading space
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(3, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("32 GB", diskSizes[1]);
Assert.AreEqual("1024 GB", diskSizes[2]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesIgnoresHeaderAndSeparatorLines()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 1024 KB ")
.AppendLine(" ") // Empty line
.AppendLine(" Disk 1 Online 1024 GB 0 B ")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(2, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("1024 GB", diskSizes[1]);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesHandlesEmptyInput()
{
Assert.Throws<ArgumentException>(() => TestWindowsDiskManager.ParseDiskSizes(string.Empty));
Assert.Throws<ArgumentException>(() => TestWindowsDiskManager.ParseDiskSizes(" "));
}

[Test]
public void WindowsDiskManagerParsesDiskSizesHandlesNoValidDisks()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsEmpty(diskSizes);
}

[Test]
public void WindowsDiskManagerParsesDiskSizesWithGptAndDynFlags()
{
string listDiskResults = new StringBuilder()
.AppendLine(" ")
.AppendLine(" Disk ### Status Size Free Dyn Gpt")
.AppendLine(" -------- ------------- ------- ------- --- ---")
.AppendLine(" Disk 0 Online 127 GB 0 B ")
.AppendLine(" Disk 1 Online 1024 GB 0 B * ") // GPT flag
.AppendLine(" Disk 2 Online 2048 GB 0 B * ") // Dyn flag
.AppendLine(" Disk 3 Online 512 GB 0 B * * ") // Both flags
.ToString();

IDictionary<int, string> diskSizes = TestWindowsDiskManager.ParseDiskSizes(listDiskResults);

Assert.IsNotNull(diskSizes);
Assert.IsNotEmpty(diskSizes);
Assert.AreEqual(4, diskSizes.Count);
Assert.AreEqual("127 GB", diskSizes[0]);
Assert.AreEqual("1024 GB", diskSizes[1]);
Assert.AreEqual("2048 GB", diskSizes[2]);
Assert.AreEqual("512 GB", diskSizes[3]);
}

[Test]
public void WindowsDiskManagerParsesDiskPartListPartitionResultsCorrectly_AzureVMScenario()
{
Expand Down Expand Up @@ -1500,6 +1687,11 @@ public TestWindowsDiskManager(ProcessManager processManager)
return WindowsDiskManager.ParseDiskIndexes(diskPartOutput);
}

public new static IDictionary<int, string> ParseDiskSizes(string diskPartOutput)
{
return WindowsDiskManager.ParseDiskSizes(diskPartOutput);
}

public new static IDictionary<string, IConvertible> ParseDiskProperties(ConcurrentBuffer diskPartOutput)
{
return WindowsDiskManager.ParseDiskProperties(diskPartOutput);
Expand Down
56 changes: 56 additions & 0 deletions src/VirtualClient/VirtualClient.Core/WindowsDiskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,12 +352,18 @@ await process.WriteInput(command)
this.Logger.LogTraceMessage(process.StandardOutput.ToString(), context);
await Task.Delay(this.WaitTime * waitModifier).ConfigureAwait(false);

// Capture the list disk output to parse disk sizes
string listDiskOutput = process.StandardOutput.ToString();

IEnumerable<int> diskIndexes = WindowsDiskManager.ParseDiskIndexes(process.StandardOutput);
if (diskIndexes?.Any() != true)
{
throw new ProcessException("DiskPart 'list disk' command did not return any disks.", ErrorReason.DiskInformationNotAvailable);
}

// Parse disk sizes from the list disk output
IDictionary<int, string> diskSizes = WindowsDiskManager.ParseDiskSizes(listDiskOutput);

foreach (int diskIndex in diskIndexes)
{
// ** Select the physical disk
Expand All @@ -384,6 +390,12 @@ await process.WriteInput(command)
IDictionary<string, IConvertible> diskProperties = WindowsDiskManager.ParseDiskProperties(process.StandardOutput);
diskProperties[Disk.WindowsDiskProperties.Index] = diskIndex;

// Add the size property from the list disk output
if (diskSizes.TryGetValue(diskIndex, out string diskSize))
{
diskProperties[Disk.WindowsDiskProperties.Size] = diskSize;
}

if (!process.StandardOutput.ToString().Contains("There are no volumes", StringComparison.OrdinalIgnoreCase))
{
// ** List the partitions for this disk
Expand Down Expand Up @@ -516,6 +528,50 @@ protected static IEnumerable<int> ParseDiskIndexes(ConcurrentBuffer diskPartOutp
return diskIndexes;
}

/// <summary>
/// Parses the disk sizes from the output of the DiskPart 'list disk' command.
/// </summary>
/// <param name="diskPartOutput">The output of the DiskPart command.</param>
/// <returns>A dictionary mapping disk index to disk size string.</returns>
protected static IDictionary<int, string> ParseDiskSizes(string diskPartOutput)
{
diskPartOutput.ThrowIfNullOrWhiteSpace(nameof(diskPartOutput));

IDictionary<int, string> diskSizes = new Dictionary<int, string>();

// Example output:
// Disk ### Status Size Free Dyn Gpt
// -------- ------------- ------- ------- --- ---
// Disk 0 Online 127 GB 0 B
// Disk 1 Online 1024 GB 0 B *
// Disk 2 Online 1024 GB 1024 GB
// Disk 10 Online 512 GB 0 B *
// Disk 127 Offline 10 TB 1024 GB

string normalizedOutput = diskPartOutput.Replace("DISKPART>", string.Empty, StringComparison.OrdinalIgnoreCase);
string[] lines = normalizedOutput.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

foreach (string line in lines)
{
// Match lines that start with optional whitespace, "Disk", followed by one or more digits
// Then capture status (may contain spaces like "No Media"), then capture size
// This regex is more robust:
// - \s* allows any leading whitespace
// - \d+ captures multi-digit disk indices (0-255)
// - .+? captures status (non-greedy) including multi-word statuses like "No Media"
// - ([\d\s]+\s*[KMGTP]?B) captures sizes with various formats including KB, MB, GB, TB, PB
Match diskMatch = Regex.Match(line, @"^\s*Disk\s+(\d+)\s+(.+?)\s+([\d\s]+\s*[KMGTP]?B)", RegexOptions.IgnoreCase);
if (diskMatch.Success)
{
int diskIndex = int.Parse(diskMatch.Groups[1].Value);
string diskSize = diskMatch.Groups[3].Value.Trim();
diskSizes[diskIndex] = diskSize;
}
}

return diskSizes;
}

/// <summary>
/// Parses the physical disk properties from the output of the DiskPart 'detail disk' command.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public static Disk CreateDisk(this IFixture fixture, int diskIndex, PlatformID p
{ "Type", "SAS" },
{ "Model", "Microsoft Virtual Disk" },
{ "Index", diskIndex },
{ "Size", "1234 GB" },
{ "Disk ID", $"{{{Guid.NewGuid()}}}" },
{ "Status", "Online" },
{ "Path", diskIndex },
Expand All @@ -118,6 +119,7 @@ public static Disk CreateDisk(this IFixture fixture, int diskIndex, PlatformID p
{
diskProperties["Boot Disk"] = "Yes";
diskProperties["Crashdump Disk"] = "Yes";
diskProperties["Size"] = "123 GB";
}

break;
Expand Down
Loading