ServiceSubnetsCommand.cs 4.1 KB

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