Tim Jones пре 2 месеци
родитељ
комит
9fe4c39ba2

+ 24 - 0
RackPeek.Domain/Resources/Hardware/Server/GetServerSystemTreeUseCase.cs

@@ -0,0 +1,24 @@
+using RackPeek.Domain.Resources.SystemResources;
+
+namespace RackPeek.Domain.Resources.Hardware.Server;
+
+public class GetServerSystemTreeUseCase(
+    IHardwareRepository hardwareRepository,
+    ISystemRepository systemRepository)
+{
+    public async Task<HardwareDependencyTree?> ExecuteAsync(string hardwareName)
+    {
+        var server = await hardwareRepository.GetByNameAsync(hardwareName) as Models.Server;
+        if (server is null) return null;
+
+        var systems = await systemRepository.GetByPhysicalHostAsync(hardwareName);
+
+        return new HardwareDependencyTree(server, systems);
+    }
+}
+
+public sealed class HardwareDependencyTree(Models.Server hardware, IReadOnlyList<SystemResource> systems)
+{
+    public Models.Server Hardware { get; } = hardware;
+    public IReadOnlyList<SystemResource> Systems { get; } = systems;
+}

+ 2 - 0
RackPeek.Domain/Resources/SystemResources/ISystemRepository.cs

@@ -7,5 +7,7 @@ public interface ISystemRepository
     Task UpdateAsync(SystemResource systemResource);
     Task DeleteAsync(string name);
     Task<SystemResource?> GetByNameAsync(string name);
+    Task<IReadOnlyList<SystemResource>> GetByPhysicalHostAsync(string name);
+
 }
 

+ 0 - 0
RackPeek.Domain/Resources/SystemResources/UseCases/AddSwitchUseCase.cs → RackPeek.Domain/Resources/SystemResources/UseCases/AddSystemUseCase.cs


+ 3 - 3
RackPeek.Domain/Resources/SystemResources/UseCases/SystemReport.cs → RackPeek.Domain/Resources/SystemResources/UseCases/SystemReportUseCase.cs

@@ -1,10 +1,10 @@
 namespace RackPeek.Domain.Resources.SystemResources.UseCases;
 
 public record SystemReport(
-    IReadOnlyList<SystemHardwareRow> Systems
+    IReadOnlyList<SystemReportRow> Systems
 );
 
-public record SystemHardwareRow(
+public record SystemReportRow(
     string Name,
     string? Type,
     string? Os,
@@ -24,7 +24,7 @@ public class SystemReportUseCase(ISystemRepository repository)
         {
             var totalStorage = system.Drives?.Sum(d => d.Size) ?? 0;
 
-            return new SystemHardwareRow(
+            return new SystemReportRow(
                 system.Name,
                 system.Type,
                 system.Os,

+ 32 - 0
RackPeek/Commands/Server/ServerTreeCommand.cs

@@ -0,0 +1,32 @@
+using RackPeek.Domain.Resources.Hardware.Server;
+using Spectre.Console;
+using Spectre.Console.Cli;
+
+namespace RackPeek.Commands.Server;
+
+public sealed class ServerTreeCommand(GetServerSystemTreeUseCase useCase) : AsyncCommand<ServerNameSettings>
+{
+    public override async Task<int> ExecuteAsync(
+        CommandContext context,
+        ServerNameSettings settings,
+        CancellationToken cancellationToken)
+    {
+        var tree = await useCase.ExecuteAsync(settings.Name);
+
+        if (tree is null)
+        {
+            AnsiConsole.MarkupLine($"[red]Server '{settings.Name}' not found.[/]");
+            return -1;
+        }
+
+        var root = new Tree($"[bold]{tree.Hardware.Name}[/]");
+
+        foreach (var system in tree.Systems)
+        {
+            root.AddNode($"[green]System:[/] {system.Name}");
+        }
+
+        AnsiConsole.Write(root);
+        return 0;
+    }
+}

+ 6 - 1
RackPeek/Program.cs

@@ -105,7 +105,9 @@ public static class CliBootstrap
 
         services.AddScoped<UpdateServerUseCase>();
         services.AddScoped<ServerSetCommand>();
-
+        services.AddScoped<GetServerSystemTreeUseCase>();
+        services.AddScoped<ServerTreeCommand>();
+        
         // CPU use cases
         services.AddScoped<AddCpuUseCase>();
         services.AddScoped<UpdateCpuUseCase>();
@@ -213,6 +215,9 @@ public static class CliBootstrap
 
                 server.AddCommand<ServerDeleteCommand>("del")
                     .WithDescription("Delete a server");
+                
+                server.AddCommand<ServerTreeCommand>("tree")
+                    .WithDescription("Displays a dependency tree for the server.");
 
                 server.AddBranch("cpu", cpu =>
                 {

+ 8 - 0
RackPeek/Yaml/YamlSystemRepository.cs

@@ -14,6 +14,14 @@ public class YamlSystemRepository(YamlResourceCollection resources) : ISystemRep
         return Task.FromResult(resources.GetByName(name) as SystemResource);
     }
 
+    public Task<IReadOnlyList<SystemResource>> GetByPhysicalHostAsync(string physicalHostName)
+    {
+        var physicalHostNameLower = physicalHostName.ToLower().Trim();
+        var results = resources.SystemResources
+            .Where(s => s.RunsOn != null && s.RunsOn.ToLower().Equals(physicalHostNameLower)).ToList();
+        return Task.FromResult<IReadOnlyList<SystemResource>>(results);
+    }
+
     public Task AddAsync(SystemResource systemResource)
     {
         if (resources.SystemResources.Any(r =>

+ 46 - 0
Tests/EndToEnd/ServerYamlE2ETests.cs

@@ -27,9 +27,55 @@ public class ServerYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
     [Fact]
     public async Task servers_cli_workflow_test()
     {
+        await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), "");
+
+        // Add switch
+        var (output, yaml) = await ExecuteAsync("servers", "add", "node01");
+        Assert.Equal("Server 'node01' added.\n", output);
+        Assert.Contains("name: node01", yaml);
+    }
+    
+    [Fact]
+    public async Task servers_tree_cli_workflow_test()
+    {
+        await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), "");
         // Add switch
         var (output, yaml) = await ExecuteAsync("servers", "add", "node01");
         Assert.Equal("Server 'node01' added.\n", output);
         Assert.Contains("name: node01", yaml);
+        
+        (output, yaml) = await ExecuteAsync("systems", "add", "host01");
+        Assert.Equal("System 'host01' added.\n", output);
+        
+        (output, yaml) = await ExecuteAsync("systems", "add", "host02");
+        Assert.Equal("System 'host02' added.\n", output);
+        
+        (output, yaml) = await ExecuteAsync("systems", "add", "host03");
+        Assert.Equal("System 'host03' added.\n", output);
+        
+        (output, yaml) = await ExecuteAsync(
+            "systems", "set", "host01",
+            "--runs-on", "node01"
+        );
+        Assert.Equal("System 'host01' updated.\n", output);
+        (output, yaml) = await ExecuteAsync(
+            "systems", "set", "host02",
+            "--runs-on", "node01"
+        );
+        Assert.Equal("System 'host02' updated.\n", output);
+        (output, yaml) = await ExecuteAsync(
+            "systems", "set", "host03",
+            "--runs-on", "node01"
+        );
+        Assert.Equal("System 'host03' updated.\n", output);
+        
+        (output, yaml) = await ExecuteAsync("servers", "tree", "node01");
+        Assert.Equal("""
+                     node01
+                     ├── System: host01
+                     ├── System: host02
+                     └── System: host03
+                     
+                     """, output);
     }
 }

+ 2 - 0
Tests/EndToEnd/SwitchYamlE2ETests.cs

@@ -27,6 +27,8 @@ public class SwitchYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
     [Fact]
     public async Task switches_cli_workflow_test()
     {
+        await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), "");
+        
         // Add switch
         var (output, yaml) = await ExecuteAsync("switches", "add", "sw01");
         Assert.Equal("Switch 'sw01' added.\n", output);

+ 2 - 0
Tests/EndToEnd/SystemYamlE2ETests.cs

@@ -27,6 +27,8 @@ public class SystemYamlE2ETests(TempYamlCliFixture fs, ITestOutputHelper outputH
 [Fact]
 public async Task systems_cli_workflow_test()
 {
+    await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), "");
+    
     // Add system
     var (output, yaml) = await ExecuteAsync("systems", "add", "host01");
     Assert.Equal("System 'host01' added.\n", output);