ServiceSubnetsCommand.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. {
  67. var runsOn = "";
  68. if (s.RunsOn?.Count > 0) runsOn = string.Join(", ", s.RunsOn);
  69. table.AddRow(s.Name, s.Ip, runsOn);
  70. }
  71. AnsiConsole.MarkupLine($"[green]Services in {result.FilteredCidr}[/]");
  72. AnsiConsole.Write(table);
  73. return 0;
  74. }
  75. var subnets = result.Subnets;
  76. subnets = subnets
  77. .OrderByDescending(s =>
  78. {
  79. var parts = s.Cidr.Split('/');
  80. var prefix = int.Parse(parts[1]);
  81. var alloc = Math.Pow(2, 32 - prefix) - 2;
  82. return alloc <= 0 ? 0 : s.Count / alloc;
  83. })
  84. .ToList();
  85. if (subnets.Count == 0)
  86. {
  87. AnsiConsole.MarkupLine("[yellow]No subnets found.[/]");
  88. return 0;
  89. }
  90. var subnetTable = new Table()
  91. .Border(TableBorder.Rounded)
  92. .AddColumn("Subnet")
  93. .AddColumn("Services")
  94. .AddColumn("Utilization");
  95. foreach (var subnet in subnets)
  96. {
  97. var parts = subnet.Cidr.Split('/');
  98. var prefix = int.Parse(parts[1]);
  99. // allocatable addresses
  100. var alloc = Math.Pow(2, 32 - prefix) - 2;
  101. double used = subnet.Count;
  102. var fullness = alloc <= 0 ? 0 : used / alloc * 100;
  103. var bar = BuildUtilizationBar(fullness);
  104. subnetTable.AddRow(subnet.Cidr, subnet.Count.ToString(), bar);
  105. }
  106. AnsiConsole.Write(subnetTable);
  107. return 0;
  108. }
  109. public class Settings : CommandSettings
  110. {
  111. [CommandOption("--cidr <CIDR>")] public string? Cidr { get; set; }
  112. [CommandOption("--prefix <PREFIX>")] public int? Prefix { get; set; }
  113. }
  114. }