ServiceSubnetsCommand.cs 4.2 KB

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