AnsibleInventory.razor 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. @page "/ansible/inventory"
  2. @using RackPeek.Domain.Resources.Desktops
  3. @using RackPeek.Domain.UseCases.Ansible
  4. @inject AnsibleInventoryGeneratorUseCase InventoryUseCase
  5. <div class="border border-zinc-800 rounded p-4 bg-zinc-900 max-w-5xl mx-auto"
  6. data-testid="ansible-inventory-page">
  7. <div class="flex justify-between items-center mb-4">
  8. <div class="text-zinc-100 text-lg">
  9. Ansible Inventory Generator
  10. </div>
  11. <button class="text-sm bg-emerald-600 hover:bg-emerald-500 px-3 py-1 rounded text-white transition"
  12. data-testid="generate-inventory-button"
  13. @onclick="GenerateInventory">
  14. Generate
  15. </button>
  16. </div>
  17. <!-- Options -->
  18. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
  19. <!-- Tags -->
  20. <div>
  21. <div class="text-zinc-400 mb-1">Group By Tags</div>
  22. <input class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  23. placeholder="prod, staging, dmz"
  24. data-testid="group-by-tags-input"
  25. @bind="_groupByTagsRaw" />
  26. <div class="text-xs text-zinc-500 mt-1">
  27. Comma-separated tag names
  28. </div>
  29. </div>
  30. <!-- Labels -->
  31. <div>
  32. <div class="text-zinc-400 mb-1">Group By Labels</div>
  33. <input class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700"
  34. placeholder="env, site"
  35. data-testid="group-by-labels-input"
  36. @bind="_groupByLabelsRaw" />
  37. <div class="text-xs text-zinc-500 mt-1">
  38. Creates groups like env_prod, site_london
  39. </div>
  40. </div>
  41. <!-- Global Vars -->
  42. <div class="md:col-span-2">
  43. <div class="text-zinc-400 mb-1">Global Variables</div>
  44. <textarea class="w-full bg-zinc-800 text-zinc-200 p-2 rounded border border-zinc-700 font-mono text-sm"
  45. rows="4"
  46. placeholder="ansible_user=ansible&#10;ansible_python_interpreter=/usr/bin/python3"
  47. data-testid="global-vars-input"
  48. @bind="_globalVarsRaw">
  49. </textarea>
  50. <div class="text-xs text-zinc-500 mt-1">
  51. One per line: key=value
  52. </div>
  53. </div>
  54. </div>
  55. <!-- Errors / Warnings -->
  56. @if (_warnings.Any())
  57. {
  58. <div class="border border-red-700 bg-red-900/40 text-red-300 p-3 rounded mb-4"
  59. data-testid="inventory-warnings">
  60. <div class="font-semibold mb-1">Warnings</div>
  61. <ul class="list-disc ml-5 text-sm">
  62. @foreach (var warning in _warnings)
  63. {
  64. <li>@warning</li>
  65. }
  66. </ul>
  67. </div>
  68. }
  69. <!-- Output -->
  70. <div>
  71. <div class="text-zinc-400 mb-1">Generated Inventory</div>
  72. <textarea class="w-full bg-black text-emerald-400 p-3 rounded border border-zinc-800 font-mono text-sm"
  73. rows="18"
  74. readonly
  75. data-testid="inventory-output">
  76. @_inventoryText
  77. </textarea>
  78. </div>
  79. </div>
  80. @code {
  81. private string _groupByTagsRaw = "prod, staging";
  82. private string _groupByLabelsRaw = "env, site";
  83. private string _globalVarsRaw =
  84. @"ansible_user=ansible
  85. ansible_python_interpreter=/usr/bin/python3";
  86. private string _inventoryText = string.Empty;
  87. private List<string> _warnings = new();
  88. private async Task GenerateInventory()
  89. {
  90. try
  91. {
  92. _warnings.Clear();
  93. _inventoryText = string.Empty;
  94. var options = new InventoryOptions
  95. {
  96. GroupByTags = ParseCsv(_groupByTagsRaw),
  97. GroupByLabelKeys = ParseCsv(_groupByLabelsRaw),
  98. GlobalVars = ParseGlobalVars(_globalVarsRaw)
  99. };
  100. var result = await InventoryUseCase.ExecuteAsync(options);
  101. if (result is null)
  102. {
  103. _warnings.Add("Inventory generation returned null.");
  104. return;
  105. }
  106. _inventoryText = result.InventoryText;
  107. _warnings = result.Warnings.ToList();
  108. }
  109. catch (Exception ex)
  110. {
  111. _warnings.Clear();
  112. _warnings.Add($"Unexpected error: {ex.Message}");
  113. }
  114. }
  115. private static IReadOnlyList<string> ParseCsv(string raw)
  116. {
  117. if (string.IsNullOrWhiteSpace(raw))
  118. return [];
  119. return raw.Split(',', StringSplitOptions.RemoveEmptyEntries)
  120. .Select(x => x.Trim())
  121. .Where(x => !string.IsNullOrWhiteSpace(x))
  122. .ToArray();
  123. }
  124. private static IDictionary<string, string> ParseGlobalVars(string raw)
  125. {
  126. var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  127. if (string.IsNullOrWhiteSpace(raw))
  128. return dict;
  129. var lines = raw.Split('\n', StringSplitOptions.RemoveEmptyEntries);
  130. foreach (var line in lines)
  131. {
  132. var trimmed = line.Trim();
  133. if (string.IsNullOrWhiteSpace(trimmed)) continue;
  134. var parts = trimmed.Split('=', 2);
  135. if (parts.Length != 2) continue;
  136. var key = parts[0].Trim();
  137. var value = parts[1].Trim();
  138. if (!string.IsNullOrWhiteSpace(key))
  139. dict[key] = value;
  140. }
  141. return dict;
  142. }
  143. }