CliBootstrap.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. using System.ComponentModel.DataAnnotations;
  2. using Microsoft.Extensions.Configuration;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using RackPeek.Domain;
  5. using RackPeek.Domain.Helpers;
  6. using RackPeek.Domain.Persistence;
  7. using RackPeek.Domain.Persistence.Yaml;
  8. using RackPeek.Domain.Resources.AccessPoints;
  9. using RackPeek.Domain.Resources.Desktops;
  10. using RackPeek.Domain.Resources.Firewalls;
  11. using RackPeek.Domain.Resources.Laptops;
  12. using RackPeek.Domain.Resources.Routers;
  13. using RackPeek.Domain.Resources.Servers;
  14. using RackPeek.Domain.Resources.Services;
  15. using RackPeek.Domain.Resources.Switches;
  16. using RackPeek.Domain.Resources.SystemResources;
  17. using Shared.Rcl.Commands;
  18. using Shared.Rcl.Commands.AccessPoints;
  19. using Shared.Rcl.Commands.AccessPoints.Labels;
  20. using Shared.Rcl.Commands.AccessPoints.Rename;
  21. using Shared.Rcl.Commands.Connections;
  22. using Shared.Rcl.Commands.Desktops;
  23. using Shared.Rcl.Commands.Desktops.Cpus;
  24. using Shared.Rcl.Commands.Desktops.Drive;
  25. using Shared.Rcl.Commands.Desktops.Gpus;
  26. using Shared.Rcl.Commands.Desktops.Labels;
  27. using Shared.Rcl.Commands.Desktops.Nics;
  28. using Shared.Rcl.Commands.Desktops.Rename;
  29. using Shared.Rcl.Commands.Exporters;
  30. using Shared.Rcl.Commands.Firewalls;
  31. using Shared.Rcl.Commands.Firewalls.Labels;
  32. using Shared.Rcl.Commands.Firewalls.Ports;
  33. using Shared.Rcl.Commands.Firewalls.Rename;
  34. using Shared.Rcl.Commands.Laptops;
  35. using Shared.Rcl.Commands.Laptops.Cpus;
  36. using Shared.Rcl.Commands.Laptops.Drive;
  37. using Shared.Rcl.Commands.Laptops.Gpus;
  38. using Shared.Rcl.Commands.Laptops.Labels;
  39. using Shared.Rcl.Commands.Laptops.Rename;
  40. using Shared.Rcl.Commands.Routers;
  41. using Shared.Rcl.Commands.Routers.Labels;
  42. using Shared.Rcl.Commands.Routers.Ports;
  43. using Shared.Rcl.Commands.Routers.Rename;
  44. using Shared.Rcl.Commands.Servers;
  45. using Shared.Rcl.Commands.Servers.Cpus;
  46. using Shared.Rcl.Commands.Servers.Drives;
  47. using Shared.Rcl.Commands.Servers.Gpus;
  48. using Shared.Rcl.Commands.Servers.Labels;
  49. using Shared.Rcl.Commands.Servers.Nics;
  50. using Shared.Rcl.Commands.Servers.Rename;
  51. using Shared.Rcl.Commands.Services;
  52. using Shared.Rcl.Commands.Services.Labels;
  53. using Shared.Rcl.Commands.Services.Rename;
  54. using Shared.Rcl.Commands.Switches;
  55. using Shared.Rcl.Commands.Switches.Labels;
  56. using Shared.Rcl.Commands.Switches.Ports;
  57. using Shared.Rcl.Commands.Switches.Rename;
  58. using Shared.Rcl.Commands.Systems;
  59. using Shared.Rcl.Commands.Systems.Labels;
  60. using Shared.Rcl.Commands.Systems.Rename;
  61. using Shared.Rcl.Commands.Tags;
  62. using Shared.Rcl.Commands.Ups;
  63. using Shared.Rcl.Commands.Ups.Labels;
  64. using Shared.Rcl.Commands.Ups.Rename;
  65. using Spectre.Console;
  66. using Spectre.Console.Cli;
  67. namespace Shared.Rcl;
  68. public static class CliBootstrap {
  69. private static string[]? _lastArgs;
  70. private static CommandApp? _app;
  71. private static bool _showingHelp;
  72. public static void SetContext(string[] args, CommandApp app) {
  73. _lastArgs = args;
  74. _app = app;
  75. }
  76. public static async Task RegisterInternals(
  77. IServiceCollection services,
  78. IConfiguration configuration,
  79. string yamlDir,
  80. string yamlFile) {
  81. services.AddSingleton(configuration);
  82. var appBasePath = AppContext.BaseDirectory;
  83. var resolvedYamlDir = Path.IsPathRooted(yamlDir)
  84. ? yamlDir
  85. : Path.Combine(appBasePath, yamlDir);
  86. Directory.CreateDirectory(resolvedYamlDir);
  87. var fullYamlPath = Path.Combine(resolvedYamlDir, yamlFile);
  88. if (!File.Exists(fullYamlPath)) await File.WriteAllTextAsync(fullYamlPath, "");
  89. services.AddLogging();
  90. services.AddScoped<RackPeekConfigMigrationDeserializer>();
  91. services.AddScoped<IResourceYamlMigrationService, ResourceYamlMigrationService>();
  92. ServiceProvider b = services.BuildServiceProvider();
  93. var collection = new YamlResourceCollection(
  94. fullYamlPath,
  95. new PhysicalTextFileStore(),
  96. new ResourceCollection(),
  97. b.GetRequiredService<IResourceYamlMigrationService>());
  98. await collection.LoadAsync();
  99. services.AddSingleton<IResourceCollection>(collection);
  100. // Infrastructure
  101. services.AddYamlRepos();
  102. // Application
  103. services.AddUseCases();
  104. services.AddCommands();
  105. }
  106. public static void BuildApp(CommandApp app) {
  107. // Spectre bootstrap
  108. app.Configure(config => {
  109. config.SetApplicationName("rpk");
  110. config.ValidateExamples();
  111. config.SetApplicationVersion(RpkConstants.Version);
  112. config.SetExceptionHandler(HandleException);
  113. // Global summary
  114. config.AddCommand<GetTotalSummaryCommand>("summary")
  115. .WithDescription("Show a summarized report of all resources in the system.");
  116. // ----------------------------
  117. // Server commands (CRUD-style)
  118. // ----------------------------
  119. config.AddBranch("servers", server => {
  120. server.SetDescription("Manage servers and their components.");
  121. server.AddCommand<ServerReportCommand>("summary")
  122. .WithDescription("Show a summarized hardware report for all servers.");
  123. server.AddCommand<ServerAddCommand>("add").WithDescription("Add a new server to the inventory.");
  124. server.AddCommand<ServerGetByNameCommand>("get")
  125. .WithDescription("List all servers or retrieve a specific server by name.");
  126. server.AddCommand<ServerDescribeCommand>("describe")
  127. .WithDescription("Display detailed information about a specific server.");
  128. server.AddCommand<ServerSetCommand>("set").WithDescription("Update properties of an existing server.");
  129. server.AddCommand<ServerDeleteCommand>("del").WithDescription("Delete a server from the inventory.");
  130. server.AddCommand<ServerRenameCommand>("rename")
  131. .WithDescription("Rename a server to a new name.");
  132. server.AddCommand<ServerTreeCommand>("tree")
  133. .WithDescription("Display the dependency tree of a server.");
  134. // Server CPUs
  135. server.AddBranch("cpu", cpu => {
  136. cpu.SetDescription("Manage CPUs attached to a server.");
  137. cpu.AddCommand<ServerCpuAddCommand>("add").WithDescription("Add a CPU to a specific server.");
  138. cpu.AddCommand<ServerCpuSetCommand>("set").WithDescription("Update configuration of a server CPU.");
  139. cpu.AddCommand<ServerCpuRemoveCommand>("del").WithDescription("Remove a CPU from a server.");
  140. });
  141. // Server Drives
  142. server.AddBranch("drive", drive => {
  143. drive.SetDescription("Manage drives attached to a server.");
  144. drive.AddCommand<ServerDriveAddCommand>("add").WithDescription("Add a storage drive to a server.");
  145. drive.AddCommand<ServerDriveUpdateCommand>("set")
  146. .WithDescription("Update properties of a server drive.");
  147. drive.AddCommand<ServerDriveRemoveCommand>("del").WithDescription("Remove a drive from a server.");
  148. });
  149. // Server GPUs
  150. server.AddBranch("gpu", gpu => {
  151. gpu.SetDescription("Manage GPUs attached to a server.");
  152. gpu.AddCommand<ServerGpuAddCommand>("add").WithDescription("Add a GPU to a server.");
  153. gpu.AddCommand<ServerGpuUpdateCommand>("set").WithDescription("Update properties of a server GPU.");
  154. gpu.AddCommand<ServerGpuRemoveCommand>("del").WithDescription("Remove a GPU from a server.");
  155. });
  156. // Server NICs
  157. server.AddBranch("nic", nic => {
  158. nic.SetDescription("Manage network interface cards (NICs) for a server.");
  159. nic.AddCommand<ServerNicAddCommand>("add").WithDescription("Add a NIC to a server.");
  160. nic.AddCommand<ServerNicUpdateCommand>("set").WithDescription("Update properties of a server NIC.");
  161. nic.AddCommand<ServerNicRemoveCommand>("del").WithDescription("Remove a NIC from a server.");
  162. });
  163. // Server Labels
  164. server.AddBranch("label", label => {
  165. label.SetDescription("Manage labels on a server.");
  166. label.AddCommand<ServerLabelAddCommand>("add").WithDescription("Add a label to a server.");
  167. label.AddCommand<ServerLabelRemoveCommand>("remove")
  168. .WithDescription("Remove a label from a server.");
  169. });
  170. // Server Tags
  171. server.AddBranch("tag", tag => {
  172. tag.SetDescription("Manage tags on a server.");
  173. tag.AddCommand<TagAddCommand<Server>>("add").WithDescription("Add a tag to a server.");
  174. tag.AddCommand<TagRemoveCommand<Server>>("remove").WithDescription("Remove a tag from a server.");
  175. });
  176. });
  177. // ----------------------------
  178. // Switch commands
  179. // ----------------------------
  180. config.AddBranch("switches", switches => {
  181. switches.SetDescription("Manage network switches.");
  182. switches.AddCommand<SwitchReportCommand>("summary")
  183. .WithDescription("Show a hardware report for all switches.");
  184. switches.AddCommand<SwitchAddCommand>("add")
  185. .WithDescription("Add a new network switch to the inventory.");
  186. switches.AddCommand<SwitchGetCommand>("list").WithDescription("List all switches in the system.");
  187. switches.AddCommand<SwitchGetByNameCommand>("get")
  188. .WithDescription("Retrieve details of a specific switch by name.");
  189. switches.AddCommand<SwitchDescribeCommand>("describe")
  190. .WithDescription("Show detailed information about a switch.");
  191. switches.AddCommand<SwitchSetCommand>("set").WithDescription("Update properties of a switch.");
  192. switches.AddCommand<SwitchDeleteCommand>("del").WithDescription("Delete a switch from the inventory.");
  193. switches.AddCommand<SwitchRenameCommand>("rename")
  194. .WithDescription("Rename a switch to a new name.");
  195. switches.AddBranch("port", port => {
  196. port.SetDescription("Manage ports on a network switch.");
  197. port.AddCommand<SwitchPortAddCommand>("add").WithDescription("Add a port to a switch.");
  198. port.AddCommand<SwitchPortUpdateCommand>("set").WithDescription("Update a switch port.");
  199. port.AddCommand<SwitchPortRemoveCommand>("del").WithDescription("Remove a port from a switch.");
  200. });
  201. switches.AddBranch("label", label => {
  202. label.SetDescription("Manage labels on a switch.");
  203. label.AddCommand<SwitchLabelAddCommand>("add").WithDescription("Add a label to a switch.");
  204. label.AddCommand<SwitchLabelRemoveCommand>("remove")
  205. .WithDescription("Remove a label from a switch.");
  206. });
  207. switches.AddBranch("tag", tag => {
  208. tag.SetDescription("Manage tags on a switch.");
  209. tag.AddCommand<TagAddCommand<Switch>>("add").WithDescription("Add a tag to a switch.");
  210. tag.AddCommand<TagRemoveCommand<Switch>>("remove").WithDescription("Remove a tag from a switch.");
  211. });
  212. });
  213. // ----------------------------
  214. // Routers commands
  215. // ----------------------------
  216. config.AddBranch("routers", routers => {
  217. routers.SetDescription("Manage network routers.");
  218. routers.AddCommand<RouterReportCommand>("summary")
  219. .WithDescription("Show a hardware report for all routers.");
  220. routers.AddCommand<RouterAddCommand>("add")
  221. .WithDescription("Add a new network router to the inventory.");
  222. routers.AddCommand<RouterGetCommand>("list").WithDescription("List all routers in the system.");
  223. routers.AddCommand<RouterGetByNameCommand>("get")
  224. .WithDescription("Retrieve details of a specific router by name.");
  225. routers.AddCommand<RouterDescribeCommand>("describe")
  226. .WithDescription("Show detailed information about a router.");
  227. routers.AddCommand<RouterSetCommand>("set").WithDescription("Update properties of a router.");
  228. routers.AddCommand<RouterDeleteCommand>("del").WithDescription("Delete a router from the inventory.");
  229. routers.AddCommand<RouterRenameCommand>("rename")
  230. .WithDescription("Rename a router to a new name.");
  231. routers.AddBranch("port", port => {
  232. port.SetDescription("Manage ports on a router.");
  233. port.AddCommand<RouterPortAddCommand>("add").WithDescription("Add a port to a router.");
  234. port.AddCommand<RouterPortUpdateCommand>("set").WithDescription("Update a router port.");
  235. port.AddCommand<RouterPortRemoveCommand>("del").WithDescription("Remove a port from a router.");
  236. });
  237. routers.AddBranch("label", label => {
  238. label.SetDescription("Manage labels on a router.");
  239. label.AddCommand<RouterLabelAddCommand>("add").WithDescription("Add a label to a router.");
  240. label.AddCommand<RouterLabelRemoveCommand>("remove")
  241. .WithDescription("Remove a label from a router.");
  242. });
  243. routers.AddBranch("tag", tag => {
  244. tag.SetDescription("Manage tags on a router.");
  245. tag.AddCommand<TagAddCommand<Router>>("add").WithDescription("Add a tag to a router.");
  246. tag.AddCommand<TagRemoveCommand<Router>>("remove").WithDescription("Remove a tag from a router.");
  247. });
  248. });
  249. // ----------------------------
  250. // Firewalls commands
  251. // ----------------------------
  252. config.AddBranch("firewalls", firewalls => {
  253. firewalls.SetDescription("Manage firewalls.");
  254. firewalls.AddCommand<FirewallReportCommand>("summary")
  255. .WithDescription("Show a hardware report for all firewalls.");
  256. firewalls.AddCommand<FirewallAddCommand>("add").WithDescription("Add a new firewall to the inventory.");
  257. firewalls.AddCommand<FirewallGetCommand>("list").WithDescription("List all firewalls in the system.");
  258. firewalls.AddCommand<FirewallGetByNameCommand>("get")
  259. .WithDescription("Retrieve details of a specific firewall by name.");
  260. firewalls.AddCommand<FirewallDescribeCommand>("describe")
  261. .WithDescription("Show detailed information about a firewall.");
  262. firewalls.AddCommand<FirewallSetCommand>("set").WithDescription("Update properties of a firewall.");
  263. firewalls.AddCommand<FirewallDeleteCommand>("del")
  264. .WithDescription("Delete a firewall from the inventory.");
  265. firewalls.AddCommand<FirewallRenameCommand>("rename")
  266. .WithDescription("Rename a firewall to a new name.");
  267. firewalls.AddBranch("port", port => {
  268. port.SetDescription("Manage ports on a firewall.");
  269. port.AddCommand<FirewallPortAddCommand>("add").WithDescription("Add a port to a firewall.");
  270. port.AddCommand<FirewallPortUpdateCommand>("set").WithDescription("Update a firewall port.");
  271. port.AddCommand<FirewallPortRemoveCommand>("del").WithDescription("Remove a port from a firewall.");
  272. });
  273. firewalls.AddBranch("label", label => {
  274. label.SetDescription("Manage labels on a firewall.");
  275. label.AddCommand<FirewallLabelAddCommand>("add").WithDescription("Add a label to a firewall.");
  276. label.AddCommand<FirewallLabelRemoveCommand>("remove")
  277. .WithDescription("Remove a label from a firewall.");
  278. });
  279. firewalls.AddBranch("tag", tag => {
  280. tag.SetDescription("Manage tags on a firewall.");
  281. tag.AddCommand<TagAddCommand<Firewall>>("add").WithDescription("Add a tag to a firewall.");
  282. tag.AddCommand<TagRemoveCommand<Firewall>>("remove")
  283. .WithDescription("Remove a tag from a firewall.");
  284. });
  285. });
  286. // ----------------------------
  287. // System commands
  288. // ----------------------------
  289. config.AddBranch("systems", system => {
  290. system.SetDescription("Manage systems and their dependencies.");
  291. system.AddCommand<SystemReportCommand>("summary")
  292. .WithDescription("Show a summary report for all systems.");
  293. system.AddCommand<SystemAddCommand>("add").WithDescription("Add a new system to the inventory.");
  294. system.AddCommand<SystemGetCommand>("list").WithDescription("List all systems.");
  295. system.AddCommand<SystemGetByNameCommand>("get").WithDescription("Retrieve a system by name.");
  296. system.AddCommand<SystemDescribeCommand>("describe")
  297. .WithDescription("Display detailed information about a system.");
  298. system.AddCommand<SystemSetCommand>("set").WithDescription("Update properties of a system.");
  299. system.AddCommand<SystemDeleteCommand>("del").WithDescription("Delete a system from the inventory.");
  300. system.AddCommand<SystemRenameCommand>("rename")
  301. .WithDescription("Rename a system to a new name.");
  302. system.AddCommand<SystemTreeCommand>("tree")
  303. .WithDescription("Display the dependency tree for a system.");
  304. system.AddBranch("label", label => {
  305. label.SetDescription("Manage labels on a system.");
  306. label.AddCommand<SystemLabelAddCommand>("add").WithDescription("Add a label to a system.");
  307. label.AddCommand<SystemLabelRemoveCommand>("remove")
  308. .WithDescription("Remove a label from a system.");
  309. });
  310. system.AddBranch("tag", tag => {
  311. tag.SetDescription("Manage tags on a system.");
  312. tag.AddCommand<TagAddCommand<SystemResource>>("add").WithDescription("Add a tag to a system.");
  313. tag.AddCommand<TagRemoveCommand<SystemResource>>("remove")
  314. .WithDescription("Remove a tag from a system.");
  315. });
  316. });
  317. // ----------------------------
  318. // Access Points
  319. // ----------------------------
  320. config.AddBranch("accesspoints", ap => {
  321. ap.SetDescription("Manage access points.");
  322. ap.AddCommand<AccessPointReportCommand>("summary")
  323. .WithDescription("Show a hardware report for all access points.");
  324. ap.AddCommand<AccessPointAddCommand>("add").WithDescription("Add a new access point.");
  325. ap.AddCommand<AccessPointGetCommand>("list").WithDescription("List all access points.");
  326. ap.AddCommand<AccessPointGetByNameCommand>("get").WithDescription("Retrieve an access point by name.");
  327. ap.AddCommand<AccessPointDescribeCommand>("describe")
  328. .WithDescription("Show detailed information about an access point.");
  329. ap.AddCommand<AccessPointSetCommand>("set").WithDescription("Update properties of an access point.");
  330. ap.AddCommand<AccessPointDeleteCommand>("del").WithDescription("Delete an access point.");
  331. ap.AddCommand<AccessPointRenameCommand>("rename")
  332. .WithDescription("Rename an access point to a new name.");
  333. ap.AddBranch("label", label => {
  334. label.SetDescription("Manage labels on an access point.");
  335. label.AddCommand<AccessPointLabelAddCommand>("add")
  336. .WithDescription("Add a label to an access point.");
  337. label.AddCommand<AccessPointLabelRemoveCommand>("remove")
  338. .WithDescription("Remove a label from an access point.");
  339. });
  340. ap.AddBranch("tag", tag => {
  341. tag.SetDescription("Manage tags on an access point.");
  342. tag.AddCommand<TagAddCommand<AccessPoint>>("add")
  343. .WithDescription("Add a tag to an access point.");
  344. tag.AddCommand<TagRemoveCommand<AccessPoint>>("remove")
  345. .WithDescription("Remove a tag from an access point.");
  346. });
  347. });
  348. // ----------------------------
  349. // UPS units
  350. // ----------------------------
  351. config.AddBranch("ups", ups => {
  352. ups.SetDescription("Manage UPS units.");
  353. ups.AddCommand<UpsReportCommand>("summary")
  354. .WithDescription("Show a hardware report for all UPS units.");
  355. ups.AddCommand<UpsAddCommand>("add").WithDescription("Add a new UPS unit.");
  356. ups.AddCommand<UpsGetCommand>("list").WithDescription("List all UPS units.");
  357. ups.AddCommand<UpsGetByNameCommand>("get").WithDescription("Retrieve a UPS unit by name.");
  358. ups.AddCommand<UpsDescribeCommand>("describe")
  359. .WithDescription("Show detailed information about a UPS unit.");
  360. ups.AddCommand<UpsSetCommand>("set").WithDescription("Update properties of a UPS unit.");
  361. ups.AddCommand<UpsDeleteCommand>("del").WithDescription("Delete a UPS unit.");
  362. ups.AddCommand<UpsRenameCommand>("rename")
  363. .WithDescription("Rename a UPS unit to a new name.");
  364. ups.AddBranch("label", label => {
  365. label.SetDescription("Manage labels on a UPS unit.");
  366. label.AddCommand<UpsLabelAddCommand>("add").WithDescription("Add a label to a UPS unit.");
  367. label.AddCommand<UpsLabelRemoveCommand>("remove")
  368. .WithDescription("Remove a label from a UPS unit.");
  369. });
  370. ups.AddBranch("tag", tag => {
  371. tag.SetDescription("Manage tags on a UPS unit.");
  372. tag.AddCommand<TagAddCommand<RackPeek.Domain.Resources.UpsUnits.Ups>>("add")
  373. .WithDescription("Add a tag to a UPS unit.");
  374. tag.AddCommand<TagRemoveCommand<RackPeek.Domain.Resources.UpsUnits.Ups>>("remove")
  375. .WithDescription("Remove a tag from a UPS unit.");
  376. });
  377. });
  378. // ----------------------------
  379. // Desktops
  380. // ----------------------------
  381. config.AddBranch("desktops", desktops => {
  382. desktops.SetDescription("Manage desktop computers and their components.");
  383. // CRUD
  384. desktops.AddCommand<DesktopAddCommand>("add").WithDescription("Add a new desktop.");
  385. desktops.AddCommand<DesktopGetCommand>("list").WithDescription("List all desktops.");
  386. desktops.AddCommand<DesktopGetByNameCommand>("get").WithDescription("Retrieve a desktop by name.");
  387. desktops.AddCommand<DesktopDescribeCommand>("describe")
  388. .WithDescription("Show detailed information about a desktop.");
  389. desktops.AddCommand<DesktopSetCommand>("set").WithDescription("Update properties of a desktop.");
  390. desktops.AddCommand<DesktopDeleteCommand>("del")
  391. .WithDescription("Delete a desktop from the inventory.");
  392. desktops.AddCommand<DesktopRenameCommand>("rename")
  393. .WithDescription("Rename a desktop to a new name.");
  394. desktops.AddCommand<DesktopReportCommand>("summary")
  395. .WithDescription("Show a summarized hardware report for all desktops.");
  396. desktops.AddCommand<DesktopTreeCommand>("tree")
  397. .WithDescription("Display the dependency tree for a desktop.");
  398. // CPU
  399. desktops.AddBranch("cpu", cpu => {
  400. cpu.SetDescription("Manage CPUs attached to desktops.");
  401. cpu.AddCommand<DesktopCpuAddCommand>("add").WithDescription("Add a CPU to a desktop.");
  402. cpu.AddCommand<DesktopCpuSetCommand>("set").WithDescription("Update a desktop CPU.");
  403. cpu.AddCommand<DesktopCpuRemoveCommand>("del").WithDescription("Remove a CPU from a desktop.");
  404. });
  405. // Drives
  406. desktops.AddBranch("drive", drive => {
  407. drive.SetDescription("Manage storage drives attached to desktops.");
  408. drive.AddCommand<DesktopDriveAddCommand>("add").WithDescription("Add a drive to a desktop.");
  409. drive.AddCommand<DesktopDriveSetCommand>("set").WithDescription("Update a desktop drive.");
  410. drive.AddCommand<DesktopDriveRemoveCommand>("del")
  411. .WithDescription("Remove a drive from a desktop.");
  412. });
  413. // GPUs
  414. desktops.AddBranch("gpu", gpu => {
  415. gpu.SetDescription("Manage GPUs attached to desktops.");
  416. gpu.AddCommand<DesktopGpuAddCommand>("add").WithDescription("Add a GPU to a desktop.");
  417. gpu.AddCommand<DesktopGpuSetCommand>("set").WithDescription("Update a desktop GPU.");
  418. gpu.AddCommand<DesktopGpuRemoveCommand>("del").WithDescription("Remove a GPU from a desktop.");
  419. });
  420. // NICs
  421. desktops.AddBranch("nic", nic => {
  422. nic.SetDescription("Manage network interface cards (NICs) for desktops.");
  423. nic.AddCommand<DesktopNicAddCommand>("add").WithDescription("Add a NIC to a desktop.");
  424. nic.AddCommand<DesktopNicSetCommand>("set").WithDescription("Update a desktop NIC.");
  425. nic.AddCommand<DesktopNicRemoveCommand>("del").WithDescription("Remove a NIC from a desktop.");
  426. });
  427. desktops.AddBranch("label", label => {
  428. label.SetDescription("Manage labels on a desktop.");
  429. label.AddCommand<DesktopLabelAddCommand>("add").WithDescription("Add a label to a desktop.");
  430. label.AddCommand<DesktopLabelRemoveCommand>("remove")
  431. .WithDescription("Remove a label from a desktop.");
  432. });
  433. desktops.AddBranch("tag", tag => {
  434. tag.SetDescription("Manage tags on a desktop.");
  435. tag.AddCommand<TagAddCommand<Desktop>>("add").WithDescription("Add a tag to a desktop.");
  436. tag.AddCommand<TagRemoveCommand<Desktop>>("remove")
  437. .WithDescription("Remove a tag from a desktop.");
  438. });
  439. });
  440. // ----------------------------
  441. // Laptops
  442. // ----------------------------
  443. config.AddBranch("laptops", laptops => {
  444. laptops.SetDescription("Manage Laptop computers and their components.");
  445. // CRUD
  446. laptops.AddCommand<LaptopAddCommand>("add").WithDescription("Add a new Laptop.");
  447. laptops.AddCommand<LaptopGetCommand>("list").WithDescription("List all Laptops.");
  448. laptops.AddCommand<LaptopGetByNameCommand>("get").WithDescription("Retrieve a Laptop by name.");
  449. laptops.AddCommand<LaptopDescribeCommand>("describe")
  450. .WithDescription("Show detailed information about a Laptop.");
  451. laptops.AddCommand<LaptopSetCommand>("set").WithDescription("Update properties of a laptop.");
  452. laptops.AddCommand<LaptopDeleteCommand>("del").WithDescription("Delete a Laptop from the inventory.");
  453. laptops.AddCommand<LaptopRenameCommand>("rename")
  454. .WithDescription("Rename a Laptop to a new name.");
  455. laptops.AddCommand<LaptopReportCommand>("summary")
  456. .WithDescription("Show a summarized hardware report for all Laptops.");
  457. laptops.AddCommand<LaptopTreeCommand>("tree")
  458. .WithDescription("Display the dependency tree for a Laptop.");
  459. // CPU
  460. laptops.AddBranch("cpu", cpu => {
  461. cpu.SetDescription("Manage CPUs attached to Laptops.");
  462. cpu.AddCommand<LaptopCpuAddCommand>("add").WithDescription("Add a CPU to a Laptop.");
  463. cpu.AddCommand<LaptopCpuSetCommand>("set").WithDescription("Update a Laptop CPU.");
  464. cpu.AddCommand<LaptopCpuRemoveCommand>("del").WithDescription("Remove a CPU from a Laptop.");
  465. });
  466. // Drives
  467. laptops.AddBranch("drive", drive => {
  468. drive.SetDescription("Manage storage drives attached to Laptops.");
  469. drive.AddCommand<LaptopDriveAddCommand>("add").WithDescription("Add a drive to a Laptop.");
  470. drive.AddCommand<LaptopDriveSetCommand>("set").WithDescription("Update a Laptop drive.");
  471. drive.AddCommand<LaptopDriveRemoveCommand>("del").WithDescription("Remove a drive from a Laptop.");
  472. });
  473. // GPUs
  474. laptops.AddBranch("gpu", gpu => {
  475. gpu.SetDescription("Manage GPUs attached to Laptops.");
  476. gpu.AddCommand<LaptopGpuAddCommand>("add").WithDescription("Add a GPU to a Laptop.");
  477. gpu.AddCommand<LaptopGpuSetCommand>("set").WithDescription("Update a Laptop GPU.");
  478. gpu.AddCommand<LaptopGpuRemoveCommand>("del").WithDescription("Remove a GPU from a Laptop.");
  479. });
  480. laptops.AddBranch("label", label => {
  481. label.SetDescription("Manage labels on a laptop.");
  482. label.AddCommand<LaptopLabelAddCommand>("add").WithDescription("Add a label to a laptop.");
  483. label.AddCommand<LaptopLabelRemoveCommand>("remove")
  484. .WithDescription("Remove a label from a laptop.");
  485. });
  486. laptops.AddBranch("tag", tag => {
  487. tag.SetDescription("Manage tags on a laptop.");
  488. tag.AddCommand<TagAddCommand<Laptop>>("add").WithDescription("Add a tag to a laptop.");
  489. tag.AddCommand<TagRemoveCommand<Laptop>>("remove")
  490. .WithDescription("Remove a tag from a laptop.");
  491. });
  492. });
  493. // ----------------------------
  494. // Services
  495. // ----------------------------
  496. config.AddBranch("services", service => {
  497. service.SetDescription("Manage services and their configurations.");
  498. service.AddCommand<ServiceReportCommand>("summary")
  499. .WithDescription("Show a summary report for all services.");
  500. service.AddCommand<ServiceAddCommand>("add").WithDescription("Add a new service.");
  501. service.AddCommand<ServiceGetCommand>("list").WithDescription("List all services.");
  502. service.AddCommand<ServiceGetByNameCommand>("get").WithDescription("Retrieve a service by name.");
  503. service.AddCommand<ServiceDescribeCommand>("describe")
  504. .WithDescription("Show detailed information about a service.");
  505. service.AddCommand<ServiceSetCommand>("set").WithDescription("Update properties of a service.");
  506. service.AddCommand<ServiceDeleteCommand>("del").WithDescription("Delete a service.");
  507. service.AddCommand<ServiceRenameCommand>("rename")
  508. .WithDescription("Rename a service to a new name.");
  509. service.AddCommand<ServiceSubnetsCommand>("subnets")
  510. .WithDescription("List subnets associated with a service, optionally filtered by CIDR.");
  511. service.AddBranch("label", label => {
  512. label.SetDescription("Manage labels on a service.");
  513. label.AddCommand<ServiceLabelAddCommand>("add").WithDescription("Add a label to a service.");
  514. label.AddCommand<ServiceLabelRemoveCommand>("remove")
  515. .WithDescription("Remove a label from a service.");
  516. });
  517. service.AddBranch("tag", tag => {
  518. tag.SetDescription("Manage tags on a service.");
  519. tag.AddCommand<TagAddCommand<Service>>("add").WithDescription("Add a tag to a service.");
  520. tag.AddCommand<TagRemoveCommand<Service>>("remove")
  521. .WithDescription("Remove a tag from a service.");
  522. });
  523. });
  524. // ----------------------------
  525. // Ansible
  526. // ----------------------------
  527. config.AddBranch("ansible", ansible => {
  528. ansible.SetDescription("Generate and manage Ansible inventory.");
  529. ansible.AddCommand<GenerateAnsibleInventoryCommand>("inventory")
  530. .WithDescription("Generate an Ansible inventory.");
  531. });
  532. config.AddBranch("ssh", ssh => {
  533. ssh.SetDescription("Generate SSH configuration from infrastructure.");
  534. ssh.AddCommand<GenerateSshConfigCommand>("export")
  535. .WithDescription("Generate an SSH config file.");
  536. });
  537. config.AddBranch("hosts", hosts => {
  538. hosts.SetDescription("Generate a hosts file from infrastructure.");
  539. hosts.AddCommand<GenerateHostsFileCommand>("export")
  540. .WithDescription("Generate a /etc/hosts compatible file.");
  541. });
  542. // ----------------------------
  543. // Tags discovery
  544. // ----------------------------
  545. config.AddBranch("tags", tags => {
  546. tags.SetDescription("Discover tags across resources.");
  547. tags.AddCommand<TagsListCommand>("list")
  548. .WithDescription("List all tags in use with usage counts.");
  549. tags.AddCommand<TagsShowCommand>("show")
  550. .WithDescription("List resources carrying a specific tag.");
  551. });
  552. config.AddBranch("connections", connections => {
  553. connections.SetDescription("Manage physical or logical port connections.");
  554. connections.AddCommand<ConnectionAddCommand>("add")
  555. .WithDescription("Create a connection between two ports.");
  556. connections.AddCommand<ConnectionRemoveCommand>("remove")
  557. .WithDescription("Remove the connection from a specific port.");
  558. });
  559. });
  560. }
  561. private static int HandleException(Exception ex, ITypeResolver? arg2) {
  562. switch (ex) {
  563. case ValidationException ve:
  564. AnsiConsole.MarkupLine($"[yellow]Validation error:[/] {ve.Message}");
  565. return 2;
  566. case ConflictException ce:
  567. AnsiConsole.MarkupLine($"[red]Conflict:[/] {ce.Message}");
  568. return 3;
  569. case NotFoundException ne:
  570. AnsiConsole.MarkupLine($"[red]Not found:[/] {ne.Message}");
  571. return 4;
  572. case CommandParseException pe:
  573. if (_showingHelp) return 1; // suppress errors during help lookup
  574. AnsiConsole.MarkupLine($"[red]Invalid command:[/] {pe.Message}");
  575. if (pe.Pretty != null) AnsiConsole.Write(pe.Pretty);
  576. ShowContextualHelp();
  577. return 1;
  578. case CommandRuntimeException re:
  579. if (_showingHelp) return 1;
  580. AnsiConsole.MarkupLine($"[red]Error:[/] {re.Message}");
  581. if (re.Pretty != null) AnsiConsole.Write(re.Pretty);
  582. ShowContextualHelp();
  583. return 1;
  584. default:
  585. AnsiConsole.MarkupLine("[red]Unexpected error occurred.[/]");
  586. AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
  587. return 99;
  588. }
  589. }
  590. private static void ShowContextualHelp() {
  591. if (_lastArgs == null || _app == null || _showingHelp) return;
  592. _showingHelp = true;
  593. try {
  594. // Extract command path (args before any --flags)
  595. var commandPath = _lastArgs.TakeWhile(a => !a.StartsWith("-")).ToList();
  596. // Try progressively shorter command paths until --help succeeds
  597. while (commandPath.Count > 0) {
  598. var helpArgs = commandPath.Append("--help").ToArray();
  599. AnsiConsole.WriteLine();
  600. var result = _app.Run(helpArgs);
  601. if (result == 0) return;
  602. commandPath.RemoveAt(commandPath.Count - 1);
  603. }
  604. }
  605. finally {
  606. _showingHelp = false;
  607. }
  608. }
  609. }