ServiceSubnetsCommand.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. using Microsoft.Extensions.DependencyInjection;
  2. using Microsoft.Extensions.Logging;
  3. using RackPeek.Domain.Resources.Services.UseCases;
  4. using Spectre.Console;
  5. using Spectre.Console.Cli;
  6. namespace Shared.Rcl.Commands.Services;
  7. public class ServiceSubnetsCommand(
  8. ILogger<ServiceSubnetsCommand> logger,
  9. IServiceProvider serviceProvider
  10. ) : AsyncCommand<ServiceSubnetsCommand.Settings>
  11. {
  12. private static string BuildUtilizationBar(double fullness, int width = 30)
  13. {
  14. fullness = Math.Clamp(fullness, 0, 100);
  15. var filled = (int)(width * (fullness / 100.0));
  16. var empty = width - filled;
  17. var color = fullness switch
  18. {
  19. < 50 => Color.Green,
  20. < 80 => Color.Yellow,
  21. _ => Color.Red
  22. };
  23. var filledBar = new string('█', filled);
  24. var emptyBar = new string('░', empty);
  25. return $"[{color.ToString().ToLower()}]{filledBar}[/]{emptyBar} {fullness:0}%";
  26. }
  27. private static uint IpToUInt32(string ip)
  28. {
  29. var parts = ip.Split('.');
  30. return (uint)(
  31. (int.Parse(parts[0]) << 24) |
  32. (int.Parse(parts[1]) << 16) |
  33. (int.Parse(parts[2]) << 8) |
  34. int.Parse(parts[3]));
  35. }
  36. public override async Task<int> ExecuteAsync(
  37. CommandContext context,
  38. Settings settings,
  39. CancellationToken cancellationToken)
  40. {
  41. using var scope = serviceProvider.CreateScope();
  42. var useCase = scope.ServiceProvider.GetRequiredService<ServiceSubnetsUseCase>();
  43. var result = await useCase.ExecuteAsync(settings.Cidr, settings.Prefix, cancellationToken);
  44. // Handle invalid CIDR
  45. if (result.IsInvalidCidr)
  46. {
  47. AnsiConsole.MarkupLine($"[red]Invalid CIDR:[/] {result.InvalidCidrValue}");
  48. return 1;
  49. }
  50. if (settings.Cidr is not null)
  51. {
  52. var services = result.Services
  53. .OrderBy(s => IpToUInt32(s.Ip))
  54. .ToList();
  55. if (services.Count == 0)
  56. {
  57. AnsiConsole.MarkupLine($"[yellow]No services found in {settings.Cidr}[/]");
  58. return 0;
  59. }
  60. var table = new Table()
  61. .Border(TableBorder.Rounded)
  62. .AddColumn("Name")
  63. .AddColumn("IP")
  64. .AddColumn("Runs On");
  65. foreach (var s in services)
  66. table.AddRow(s.Name, s.Ip, s.RunsOn ?? "Unknown");
  67. AnsiConsole.MarkupLine($"[green]Services in {result.FilteredCidr}[/]");
  68. AnsiConsole.Write(table);
  69. return 0;
  70. }
  71. var subnets = result.Subnets;
  72. subnets = subnets
  73. .OrderByDescending(s =>
  74. {
  75. var parts = s.Cidr.Split('/');
  76. var prefix = int.Parse(parts[1]);
  77. var alloc = Math.Pow(2, 32 - prefix) - 2;
  78. return alloc <= 0 ? 0 : s.Count / alloc;
  79. })
  80. .ToList();
  81. if (subnets.Count == 0)
  82. {
  83. AnsiConsole.MarkupLine("[yellow]No subnets found.[/]");
  84. return 0;
  85. }
  86. var subnetTable = new Table()
  87. .Border(TableBorder.Rounded)
  88. .AddColumn("Subnet")
  89. .AddColumn("Services")
  90. .AddColumn("Utilization");
  91. foreach (var subnet in subnets)
  92. {
  93. var parts = subnet.Cidr.Split('/');
  94. var prefix = int.Parse(parts[1]);
  95. // allocatable addresses
  96. var alloc = Math.Pow(2, 32 - prefix) - 2;
  97. double used = subnet.Count;
  98. var fullness = alloc <= 0 ? 0 : used / alloc * 100;
  99. var bar = BuildUtilizationBar(fullness);
  100. subnetTable.AddRow(subnet.Cidr, subnet.Count.ToString(), bar);
  101. }
  102. AnsiConsole.Write(subnetTable);
  103. return 0;
  104. }
  105. public class Settings : CommandSettings
  106. {
  107. [CommandOption("--cidr <CIDR>")] public string? Cidr { get; set; }
  108. [CommandOption("--prefix <PREFIX>")] public int? Prefix { get; set; }
  109. }
  110. }