cli.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. use clap::{Parser, Subcommand};
  2. #[derive(Parser)]
  3. #[command(author, version, about = "DGX Spark Launcher для vLLM и SGLang")]
  4. pub struct Cli {
  5. #[arg(long, short, default_value = "config.toml")]
  6. pub config: String,
  7. #[arg(long, short, help = "Показать команды без выполнения")]
  8. pub dry_run: bool,
  9. #[command(subcommand)]
  10. pub command: Commands,
  11. }
  12. #[derive(Subcommand)]
  13. pub enum Commands {
  14. /// Запустить кластер
  15. Serve {
  16. #[arg(long, required = true)]
  17. recipe: String,
  18. #[arg(long, default_value = "auto")]
  19. cluster: String,
  20. /// Keep containers after stop (default: auto-remove on stop)
  21. #[arg(long, default_value = "false")]
  22. keep: bool,
  23. /// Skip pre-serve hooks (cluster-level + recipe-level).
  24. #[arg(long, default_value = "false")]
  25. skip_before_serve: bool,
  26. },
  27. /// Показать логи контейнеров
  28. Logs {
  29. #[arg(long, required = true)]
  30. recipe: String,
  31. #[arg(long, action = clap::ArgAction::SetTrue)]
  32. follow: bool,
  33. },
  34. /// Остановить кластер
  35. Stop {
  36. #[arg(long, required = true)]
  37. recipe: String,
  38. },
  39. /// Проверить здоровье кластера
  40. Health {
  41. #[arg(
  42. long,
  43. help = "Имя кластера (например, 4nodes-switch). Если не указано - проверяет все."
  44. )]
  45. cluster: Option<String>,
  46. #[arg(
  47. long,
  48. help = "Показать хост-ключи узлов (для добавления в config.toml)"
  49. )]
  50. fingerprint: bool,
  51. },
  52. /// Проверить и исправить рецепт (doctor mode)
  53. Doctor {
  54. #[arg(long, required = true, help = "Имя рецепта для проверки")]
  55. recipe: String,
  56. #[arg(
  57. long,
  58. default_value = "auto",
  59. help = "Кластер (если не auto — использует recipe.cluster)"
  60. )]
  61. cluster: String,
  62. #[arg(
  63. long,
  64. help = "Автоматически исправить проблемы (создать Dockerfile + собрать образ)"
  65. )]
  66. repair: bool,
  67. },
  68. /// Управление tmux-сессиями (альтернатива Docker)
  69. #[command(subcommand)]
  70. Tmux(TmuxCommands),
  71. /// Auto-discover host keys and write them into config.toml
  72. InitHostKeys {
  73. #[arg(
  74. long,
  75. help = "Имя кластера (например, 4nodes-switch). Если не указано — все."
  76. )]
  77. cluster: Option<String>,
  78. },
  79. /// Run an arbitrary shell command on a cluster or single node
  80. Exec {
  81. /// Cluster name (mutually exclusive with --recipe).
  82. #[arg(long, conflicts_with = "recipe")]
  83. cluster: Option<String>,
  84. /// Recipe name; resolves to the recipe's pinned cluster.
  85. #[arg(long, conflicts_with = "cluster")]
  86. recipe: Option<String>,
  87. /// Target a specific node within the cluster (default: all).
  88. #[arg(long)]
  89. node: Option<String>,
  90. /// Name of an alias from `[exec_aliases]` in config.toml.
  91. /// Mutually exclusive with a trailing command.
  92. #[arg(long)]
  93. alias: Option<String>,
  94. /// Command to run (everything after `--`).
  95. #[arg(trailing_var_arg = true)]
  96. command: Vec<String>,
  97. },
  98. /// Power on/off nodes via Home Assistant (smart plugs / Wake-on-LAN).
  99. Power {
  100. #[command(subcommand)]
  101. action: PowerAction,
  102. },
  103. }
  104. #[derive(Subcommand)]
  105. pub enum PowerAction {
  106. /// Power off: SSH `sudo shutdown now` + wait + HA `turn_off`.
  107. Off {
  108. #[arg(long)]
  109. cluster: String,
  110. /// Target a specific node within the cluster (default: all).
  111. #[arg(long)]
  112. node: Option<String>,
  113. },
  114. /// Power on: HA `turn_on` only (no SSH; the smart plug wakes the machine).
  115. On {
  116. #[arg(long)]
  117. cluster: String,
  118. /// Target a specific node within the cluster (default: all).
  119. #[arg(long)]
  120. node: Option<String>,
  121. },
  122. /// Switch clusters: disable nodes from source, enable nodes in target.
  123. Switch {
  124. /// Current cluster (nodes + entities to shut down).
  125. #[arg(long)]
  126. from: String,
  127. /// Target cluster (nodes + entities to wake up).
  128. #[arg(long)]
  129. to: String,
  130. },
  131. }
  132. #[derive(Subcommand)]
  133. pub enum TmuxCommands {
  134. /// Запустить кластер через tmux
  135. Serve {
  136. #[arg(long, required = true)]
  137. recipe: String,
  138. #[arg(long, default_value = "auto")]
  139. cluster: String,
  140. /// Skip pre-serve hooks (cluster-level + recipe-level).
  141. #[arg(long, default_value = "false")]
  142. skip_before_serve: bool,
  143. },
  144. /// Остановить tmux-сессии
  145. Stop {
  146. #[arg(long, required = true)]
  147. recipe: String,
  148. #[arg(long, default_value = "auto")]
  149. cluster: String,
  150. },
  151. /// Статус tmux-сессий (подробный)
  152. Status {
  153. #[arg(long, required = true)]
  154. recipe: String,
  155. #[arg(long, default_value = "auto")]
  156. cluster: String,
  157. },
  158. /// Показать логи tmux-сессий
  159. Logs {
  160. #[arg(long, required = true)]
  161. recipe: String,
  162. #[arg(long, action = clap::ArgAction::SetTrue)]
  163. follow: bool,
  164. #[arg(long, default_value = "auto")]
  165. cluster: String,
  166. },
  167. /// Подключиться к tmux-сессии головного узла
  168. Attach {
  169. #[arg(long, required = true)]
  170. recipe: String,
  171. #[arg(long, default_value = "auto")]
  172. cluster: String,
  173. },
  174. }