ganeti.scm 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020, 2022 Marius Bakke <marius@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (gnu services ganeti)
  19. #:use-module (gnu packages virtualization)
  20. #:use-module (gnu services)
  21. #:use-module (gnu services mcron)
  22. #:use-module (gnu services shepherd)
  23. #:use-module (guix gexp)
  24. #:use-module (guix records)
  25. #:use-module (srfi srfi-1)
  26. #:use-module (ice-9 match)
  27. #:export (ganeti-noded-configuration
  28. ganeti-noded-configuration?
  29. ganeti-noded-configuration-ganeti
  30. ganeti-noded-configuration-port
  31. ganeti-noded-configuration-address
  32. ganeti-noded-configuration-interface
  33. ganeti-noded-configuration-max-clients
  34. ganeti-noded-configuration-ssl?
  35. ganeti-noded-configuration-ssl-key
  36. ganeti-noded-configuration-ssl-cert
  37. ganeti-noded-configuration-debug?
  38. ganeti-noded-service-type
  39. ganeti-confd-configuration
  40. ganeti-confd-configuration?
  41. ganeti-confd-configuration-ganeti
  42. ganeti-confd-configuration-port
  43. ganeti-confd-configuration-address
  44. ganeti-confd-configuration-debug
  45. ganeti-confd-service-type
  46. ganeti-wconfd-configuration
  47. ganeti-wconfd-configuration?
  48. ganeti-wconfd-configuration-ganeti
  49. ganeti-wconfd-configuration-no-voting?
  50. ganeti-wconfd-configuration-debug?
  51. ganeti-wconfd-service-type
  52. ganeti-luxid-configuration
  53. ganeti-luxid-configuration?
  54. ganeti-luxid-configuration-ganeti
  55. ganeti-luxid-configuration-no-voting?
  56. ganeti-luxid-configuration-debug?
  57. ganeti-luxid-service-type
  58. ganeti-rapi-configuration
  59. ganeti-rapi-configuration?
  60. ganeti-rapi-configuration-ganeti
  61. ganeti-rapi-configuration-require-authentication?
  62. ganeti-rapi-configuration-port
  63. ganeti-rapi-configuration-address
  64. ganeti-rapi-configuration-interface
  65. ganeti-rapi-configuration-max-clients
  66. ganeti-rapi-configuration-ssl?
  67. ganeti-rapi-configuration-ssl-key
  68. ganeti-rapi-configuration-ssl-cert
  69. ganeti-rapi-configuration-debug?
  70. ganeti-rapi-service-type
  71. ganeti-kvmd-configuration
  72. ganeti-kvmd-configuration?
  73. ganeti-kvmd-configuration-ganeti
  74. ganeti-kvmd-configuration-debug?
  75. ganeti-kvmd-service-type
  76. ganeti-mond-configuration
  77. ganeti-mond-configuration?
  78. ganeti-mond-configuration-ganeti
  79. ganeti-mond-configuration-port
  80. ganeti-mond-configuration-address
  81. ganeti-mond-configuration-debug?
  82. ganeti-mond-service-type
  83. ganeti-metad-configuration
  84. ganeti-metad-configuration?
  85. ganeti-metad-configuration-ganeti
  86. ganeti-metad-configuration-port
  87. ganeti-metad-configuration-address
  88. ganeti-metad-configuration-debug?
  89. ganeti-metad-service-type
  90. ganeti-watcher-configuration
  91. ganeti-watcher-configuration?
  92. ganeti-watcher-configuration-ganeti
  93. ganeti-watcher-configuration-schedule
  94. ganeti-watcher-configuration-rapi-ip
  95. ganeti-watcher-configuration-job-age
  96. ganeti-watcher-configuration-verify-disks?
  97. ganeti-watcher-configuration-debug?
  98. ganeti-watcher-service-type
  99. ganeti-cleaner-configuration
  100. ganeti-cleaner-configuration?
  101. ganeti-cleaner-configuration-ganeti
  102. ganeti-cleaner-configuration-master-schedule
  103. ganeti-cleaner-configuration-node-schedule
  104. ganeti-cleaner-service-type
  105. ganeti-os
  106. ganeti-os?
  107. ganeti-os-name
  108. ganeti-os-extension
  109. ganeti-os-variants
  110. ganeti-os-variant
  111. ganeti-os-variant?
  112. ganeti-os-variant-name
  113. ganeti-os-variant-configuration
  114. %debootstrap-interfaces-hook
  115. %debootstrap-grub-hook
  116. %default-debootstrap-hooks
  117. %default-debootstrap-extra-pkgs
  118. debootstrap-configuration
  119. debootstrap-configuration?
  120. debootstrap-configuration-hooks
  121. debootstrap-configuration-proxy
  122. debootstrap-configuration-mirror
  123. debootstrap-configuration-arch
  124. debootstrap-configuration-suite
  125. debootstrap-configuration-extra-pkgs
  126. debootstrap-configuration-components
  127. debootstrap-configuration-generate-cache?
  128. debootstrap-configuration-clean-cache
  129. debootstrap-configuration-partition-style
  130. debootstrap-configuration-partition-alignment
  131. debootstrap-variant
  132. debootstrap-os
  133. %default-debootstrap-variants
  134. guix-variant
  135. guix-os
  136. %default-guix-variants
  137. %default-ganeti-os
  138. ganeti-configuration
  139. ganeti-configuration?
  140. ganeti-configuration-noded-configuration
  141. ganeti-configuration-confd-configuration
  142. ganeti-configuration-wconfd-configuration
  143. ganeti-configuration-luxid-configuration
  144. ganeti-configuration-rapi-configuration
  145. ganeti-configuration-kvmd-configuration
  146. ganeti-configuration-mond-configuration
  147. ganeti-configuration-metad-configuration
  148. ganeti-configuration-watcher-configuration
  149. ganeti-configuration-cleaner-configuration
  150. ganeti-configuration-file-storage-paths
  151. ganeti-configuration-os
  152. ganeti-service-type))
  153. ;;;
  154. ;;; Service definitions for running a Ganeti cluster.
  155. ;;;
  156. ;;; Planned improvements: run daemons (except ganeti-noded) under unprivileged
  157. ;;; user accounts and/or containers. The account names must match the ones
  158. ;;; given to Ganetis configure script. metad needs "setcap" or root in order
  159. ;;; to bind on port 80.
  160. ;; Set PATH so the various daemons are able to find the 'ip' executable, LVM,
  161. ;; Ceph, Gluster, etc, without having to add absolute references to everything.
  162. (define %default-ganeti-environment-variables
  163. (list (string-append "PATH="
  164. (string-join '("/run/setuid-programs"
  165. "/run/current-system/profile/sbin"
  166. "/run/current-system/profile/bin")
  167. ":"))))
  168. (define-record-type* <ganeti-noded-configuration>
  169. ganeti-noded-configuration make-ganeti-noded-configuration
  170. ganeti-noded-configuration?
  171. (ganeti ganeti-noded-configuration-ganeti ;file-like
  172. (default ganeti))
  173. (port ganeti-noded-configuration-port ;integer
  174. (default 1811))
  175. (address ganeti-noded-configuration-address ;string
  176. (default "0.0.0.0"))
  177. (interface ganeti-noded-configuration-interface ;string | #f
  178. (default #f))
  179. (max-clients ganeti-noded-configuration-max-clients ;integer
  180. (default 20))
  181. (ssl? ganeti-noded-configuration-ssl? ;Boolean
  182. (default #t))
  183. (ssl-key ganeti-noded-configuration-ssl-key ;string
  184. (default "/var/lib/ganeti/server.pem"))
  185. (ssl-cert ganeti-noded-configuration-ssl-cert ;string
  186. (default "/var/lib/ganeti/server.pem"))
  187. (debug? ganeti-noded-configuration-debug? ;Boolean
  188. (default #f)))
  189. (define ganeti-noded-service
  190. (match-lambda
  191. (($ <ganeti-noded-configuration> ganeti port address interface max-clients
  192. ssl? ssl-key ssl-cert debug?)
  193. (list (shepherd-service
  194. (documentation "Run the Ganeti node daemon.")
  195. (provision '(ganeti-noded))
  196. (requirement '(user-processes networking))
  197. ;; If the daemon stops, it is probably for a good reason;
  198. ;; otherwise ganeti-watcher will restart it for us anyway.
  199. (respawn? #f)
  200. (start #~(make-forkexec-constructor
  201. (list #$(file-append ganeti "/sbin/ganeti-noded")
  202. #$(string-append "--port=" (number->string port))
  203. #$(string-append "--bind=" address)
  204. #$@(if interface
  205. #~((string-append "--interface=" #$interface))
  206. #~())
  207. #$(string-append "--max-clients="
  208. (number->string max-clients))
  209. #$@(if ssl?
  210. #~((string-append "--ssl-key=" #$ssl-key)
  211. (string-append "--ssl-cert=" #$ssl-cert))
  212. #~("--no-ssl"))
  213. #$@(if debug?
  214. #~("--debug")
  215. #~()))
  216. #:environment-variables
  217. '#$%default-ganeti-environment-variables
  218. #:pid-file "/var/run/ganeti/ganeti-noded.pid"))
  219. (stop #~(make-kill-destructor)))))))
  220. (define ganeti-noded-service-type
  221. (service-type (name 'ganeti-noded)
  222. (extensions
  223. (list (service-extension shepherd-root-service-type
  224. ganeti-noded-service)))
  225. (default-value (ganeti-noded-configuration))
  226. (description
  227. "@command{ganeti-noded} is the daemon which is responsible
  228. for the node functions in the Ganeti system.")))
  229. (define-record-type* <ganeti-confd-configuration>
  230. ganeti-confd-configuration make-ganeti-confd-configuration
  231. ganeti-confd-configuration?
  232. (ganeti ganeti-confd-configuration-ganeti ;file-like
  233. (default ganeti))
  234. (port ganeti-confd-configuration-port ;integer
  235. (default 1814))
  236. (address ganeti-confd-configuration-address ;string
  237. (default "0.0.0.0"))
  238. (debug? ganeti-confd-configuration-debug? ;Boolean
  239. (default #f)))
  240. (define ganeti-confd-service
  241. (match-lambda
  242. (($ <ganeti-confd-configuration> ganeti port address debug?)
  243. (list (shepherd-service
  244. (documentation "Run the Ganeti confd daemon.")
  245. (provision '(ganeti-confd))
  246. (requirement '(user-processes networking))
  247. (respawn? #f)
  248. (start #~(make-forkexec-constructor
  249. (list #$(file-append ganeti "/sbin/ganeti-confd")
  250. #$(string-append "--port=" (number->string port))
  251. #$(string-append "--bind=" address)
  252. #$@(if debug?
  253. #~("--debug")
  254. #~()))
  255. #:environment-variables
  256. '#$%default-ganeti-environment-variables
  257. #:pid-file "/var/run/ganeti/ganeti-confd.pid"))
  258. (stop #~(make-kill-destructor)))))))
  259. (define ganeti-confd-service-type
  260. (service-type (name 'ganeti-confd)
  261. (extensions
  262. (list (service-extension shepherd-root-service-type
  263. ganeti-confd-service)))
  264. (default-value (ganeti-confd-configuration))
  265. (description
  266. "@command{ganeti-confd} is a daemon used to answer queries
  267. related to the configuration of a Ganeti cluster.")))
  268. (define-record-type* <ganeti-wconfd-configuration>
  269. ganeti-wconfd-configuration make-ganeti-wconfd-configuration
  270. ganeti-wconfd-configuration?
  271. (ganeti ganeti-wconfd-configuration-ganeti ;file-like
  272. (default ganeti))
  273. (no-voting? ganeti-wconfd-configuration-no-voting? ;Boolean
  274. (default #f))
  275. (debug? ganeti-wconfd-configuration-debug? ;Boolean
  276. (default #f)))
  277. ;; If this file exists, the wconfd daemon will be forcefully started even on
  278. ;; non-master nodes. It is used to accommodate a master-failover scenario.
  279. (define %wconfd-force-node-hint
  280. "/var/lib/ganeti/guix_wconfd_force_node_hint")
  281. (define (wconfd-wrapper ganeti args)
  282. ;; Wrapper for the wconfd daemon that looks for the force-node hint.
  283. (program-file
  284. "wconfd-wrapper"
  285. #~(begin
  286. (let ((wconfd #$(file-append ganeti "/sbin/ganeti-wconfd"))
  287. (force-node? (file-exists? #$%wconfd-force-node-hint)))
  288. (if force-node?
  289. (execl wconfd wconfd "--force-node" "--no-voting" "--yes-do-it" #$@args)
  290. (execl wconfd wconfd #$@args))))))
  291. (define shepherd-wconfd-force-start-action
  292. ;; Shepherd action to create the force-node hint and start wconfd.
  293. (shepherd-action
  294. (name 'force-start)
  295. (documentation
  296. "Forcefully start wconfd even on non-master nodes (dangerous!).")
  297. (procedure #~(lambda _
  298. (format #t "Forcefully starting the wconfd daemon...~%")
  299. (action 'ganeti-wconfd 'enable)
  300. (dynamic-wind
  301. (lambda ()
  302. (false-if-exception
  303. (call-with-output-file #$%wconfd-force-node-hint
  304. (lambda (port)
  305. (const #t)))))
  306. (lambda ()
  307. (action 'ganeti-wconfd 'restart))
  308. (lambda ()
  309. (delete-file #$%wconfd-force-node-hint)))
  310. #t))))
  311. (define ganeti-wconfd-service
  312. (match-lambda
  313. (($ <ganeti-wconfd-configuration> ganeti no-voting? debug?)
  314. (list (shepherd-service
  315. (documentation "Run the Ganeti wconfd daemon.")
  316. (provision '(ganeti-wconfd))
  317. (requirement '(user-processes))
  318. ;; Shepherd action to support a master-failover scenario. It is
  319. ;; automatically invoked during 'gnt-cluster master-failover' (see
  320. ;; related Ganeti patch) and not intended for interactive use.
  321. (actions (list shepherd-wconfd-force-start-action))
  322. ;; wconfd will disable itself when not running on the master
  323. ;; node. Don't attempt to restart it.
  324. (respawn? #f)
  325. (start
  326. #~(make-forkexec-constructor
  327. (list #$(wconfd-wrapper ganeti
  328. (append
  329. (if no-voting?
  330. '("--no-voting" "--yes-do-it")
  331. '())
  332. (if debug?
  333. '("--debug")
  334. '()))))
  335. #:environment-variables
  336. '#$%default-ganeti-environment-variables
  337. #:pid-file "/var/run/ganeti/ganeti-wconfd.pid"))
  338. (stop #~(make-kill-destructor)))))))
  339. (define ganeti-wconfd-service-type
  340. (service-type (name 'ganeti-wconfd)
  341. (extensions
  342. (list (service-extension shepherd-root-service-type
  343. ganeti-wconfd-service)))
  344. (default-value (ganeti-wconfd-configuration))
  345. (description
  346. "@command{ganeti-wconfd} is the daemon that has authoritative
  347. knowledge about the configuration and is the only entity that can accept changes
  348. to it. All jobs that need to modify the configuration will do so by sending
  349. appropriate requests to this daemon.")))
  350. (define-record-type* <ganeti-luxid-configuration>
  351. ganeti-luxid-configuration make-ganeti-luxid-configuration
  352. ganeti-luxid-configuration?
  353. (ganeti ganeti-luxid-configuration-ganeti ;file-like
  354. (default ganeti))
  355. (no-voting? ganeti-luxid-configuration-no-voting? ;Boolean
  356. (default #f))
  357. (debug? ganeti-luxid-configuration-debug? ;Boolean
  358. (default #f)))
  359. (define ganeti-luxid-service
  360. (match-lambda
  361. (($ <ganeti-luxid-configuration> ganeti no-voting? debug?)
  362. (list (shepherd-service
  363. (documentation "Run the Ganeti LUXI daemon.")
  364. (provision '(ganeti-luxid))
  365. (requirement '(user-processes))
  366. ;; This service will automatically disable itself when not
  367. ;; running on the master node. Don't attempt to restart it.
  368. (respawn? #f)
  369. (start #~(make-forkexec-constructor
  370. (list #$(file-append ganeti "/sbin/ganeti-luxid")
  371. #$@(if no-voting?
  372. #~("--no-voting" "--yes-do-it")
  373. #~())
  374. #$@(if debug?
  375. #~("--debug")
  376. #~()))
  377. #:environment-variables
  378. '#$%default-ganeti-environment-variables
  379. #:pid-file "/var/run/ganeti/ganeti-luxid.pid"))
  380. (stop #~(make-kill-destructor)))))))
  381. (define ganeti-luxid-service-type
  382. (service-type (name 'ganeti-luxid)
  383. (extensions
  384. (list (service-extension shepherd-root-service-type
  385. ganeti-luxid-service)))
  386. (default-value (ganeti-luxid-configuration))
  387. (description
  388. "@command{ganeti-luxid} is a daemon used to answer queries
  389. related to the configuration and the current live state of a Ganeti cluster.
  390. Additionally, it is the authoritative daemon for the Ganeti job queue. Jobs can
  391. be submitted via this daemon and it schedules and starts them.")))
  392. (define-record-type* <ganeti-rapi-configuration>
  393. ganeti-rapi-configuration make-ganeti-rapi-configuration
  394. ganeti-rapi-configuration?
  395. (ganeti ganeti-rapi-configuration-ganeti ;file-like
  396. (default ganeti))
  397. (require-authentication?
  398. ganeti-rapi-configuration-require-authentication? ;Boolean
  399. (default #f))
  400. (port ganeti-rapi-configuration-port ;integer
  401. (default 5080))
  402. (address ganeti-rapi-configuration-address ;string
  403. (default "0.0.0.0"))
  404. (interface ganeti-rapi-configuration-interface ;string | #f
  405. (default #f))
  406. (max-clients ganeti-rapi-configuration-max-clients ;integer
  407. (default 20))
  408. (ssl? ganeti-rapi-configuration-ssl? ;Boolean
  409. (default #t))
  410. (ssl-key ganeti-rapi-configuration-ssl-key ;string
  411. (default "/var/lib/ganeti/server.pem"))
  412. (ssl-cert ganeti-rapi-configuration-ssl-cert ;string
  413. (default "/var/lib/ganeti/server.pem"))
  414. (debug? ganeti-rapi-configuration-debug? ;Boolean
  415. (default #f)))
  416. (define ganeti-rapi-service
  417. (match-lambda
  418. (($ <ganeti-rapi-configuration> ganeti require-authentication? port address
  419. interface max-clients ssl? ssl-key ssl-cert
  420. debug?)
  421. (list (shepherd-service
  422. (documentation "Run the Ganeti RAPI daemon.")
  423. (provision '(ganeti-rapi))
  424. (requirement '(user-processes networking))
  425. ;; This service will automatically disable itself when not
  426. ;; running on the master node. Don't attempt to restart it.
  427. (respawn? #f)
  428. (start #~(make-forkexec-constructor
  429. (list #$(file-append ganeti "/sbin/ganeti-rapi")
  430. #$@(if require-authentication?
  431. #~("--require-authentication")
  432. #~())
  433. #$(string-append "--port=" (number->string port))
  434. #$(string-append "--bind=" address)
  435. #$@(if interface
  436. #~((string-append "--interface=" #$interface))
  437. #~())
  438. #$(string-append "--max-clients="
  439. (number->string max-clients))
  440. #$@(if ssl?
  441. #~((string-append "--ssl-key=" #$ssl-key)
  442. (string-append "--ssl-cert=" #$ssl-cert))
  443. #~("--no-ssl"))
  444. #$@(if debug?
  445. #~("--debug")
  446. #~()))
  447. #:environment-variables
  448. '#$%default-ganeti-environment-variables
  449. #:pid-file "/var/run/ganeti/ganeti-rapi.pid"))
  450. (stop #~(make-kill-destructor)))))))
  451. (define ganeti-rapi-service-type
  452. (service-type (name 'ganeti-rapi)
  453. (extensions
  454. (list (service-extension shepherd-root-service-type
  455. ganeti-rapi-service)))
  456. (default-value (ganeti-rapi-configuration))
  457. (description
  458. "@command{ganeti-rapi} is the daemon providing a remote API
  459. for Ganeti clusters.")))
  460. (define-record-type* <ganeti-kvmd-configuration>
  461. ganeti-kvmd-configuration make-ganeti-kvmd-configuration
  462. ganeti-kvmd-configuration?
  463. (ganeti ganeti-kvmd-configuration-ganeti ;file-like
  464. (default ganeti))
  465. (debug? ganeti-kvmd-configuration-debug? ;Boolean
  466. (default #f)))
  467. (define ganeti-kvmd-service
  468. (match-lambda
  469. (($ <ganeti-kvmd-configuration> ganeti debug?)
  470. (list (shepherd-service
  471. (documentation "Run the Ganeti KVM daemon.")
  472. (provision '(ganeti-kvmd))
  473. (requirement '(user-processes))
  474. ;; This service will automatically disable itself when not
  475. ;; needed. Don't attempt to restart it.
  476. (respawn? #f)
  477. (start #~(make-forkexec-constructor
  478. (list #$(file-append ganeti "/sbin/ganeti-kvmd")
  479. #$@(if debug?
  480. #~("--debug")
  481. #~()))
  482. #:environment-variables
  483. '#$%default-ganeti-environment-variables
  484. #:pid-file "/var/run/ganeti/ganeti-kvmd.pid"))
  485. (stop #~(make-kill-destructor)))))))
  486. (define ganeti-kvmd-service-type
  487. (service-type (name 'ganeti-kvmd)
  488. (extensions
  489. (list (service-extension shepherd-root-service-type
  490. ganeti-kvmd-service)))
  491. (default-value (ganeti-kvmd-configuration))
  492. (description
  493. "@command{ganeti-kvmd} is responsible for determining whether
  494. a given KVM instance was shutdown by an administrator or a user.
  495. The KVM daemon monitors, using @code{inotify}, KVM instances through their QMP
  496. sockets, which are provided by KVM. Using the QMP sockets, the KVM daemon
  497. listens for particular shutdown, powerdown, and stop events which will determine
  498. if a given instance was shutdown by the user or Ganeti, and this result is
  499. communicated to Ganeti via a special file in the file system.")))
  500. (define-record-type* <ganeti-mond-configuration>
  501. ganeti-mond-configuration make-ganeti-mond-configuration
  502. ganeti-mond-configuration?
  503. (ganeti ganeti-mond-configuration-ganeti ;file-like
  504. (default ganeti))
  505. (port ganeti-mond-configuration-port ;integer
  506. (default 1815))
  507. (address ganeti-mond-configuration-address ;string
  508. (default "0.0.0.0"))
  509. (debug? ganeti-mond-configuration-debug? ;Boolean
  510. (default #f)))
  511. (define ganeti-mond-service
  512. (match-lambda
  513. (($ <ganeti-mond-configuration> ganeti port address debug?)
  514. (list (shepherd-service
  515. (documentation "Run the Ganeti monitoring daemon.")
  516. (provision '(ganeti-mond))
  517. (requirement '(user-processes networking))
  518. (respawn? #f)
  519. (start #~(make-forkexec-constructor
  520. (list #$(file-append ganeti "/sbin/ganeti-mond")
  521. #$(string-append "--port=" (number->string port))
  522. #$(string-append "--bind=" address)
  523. #$@(if debug?
  524. #~("--debug")
  525. #~()))
  526. #:pid-file "/var/run/ganeti/ganeti-mond.pid"))
  527. (stop #~(make-kill-destructor)))))))
  528. (define ganeti-mond-service-type
  529. (service-type (name 'ganeti-mond)
  530. (extensions
  531. (list (service-extension shepherd-root-service-type
  532. ganeti-mond-service)))
  533. (default-value (ganeti-mond-configuration))
  534. (description
  535. "@command{ganeti-mond} is a daemon providing monitoring
  536. functionality. It is responsible for running the data collectors and to
  537. provide the collected information through a HTTP interface.")))
  538. (define-record-type* <ganeti-metad-configuration>
  539. ganeti-metad-configuration make-ganeti-metad-configuration
  540. ganeti-metad-configuration?
  541. (ganeti ganeti-metad-configuration-ganeti ;file-like
  542. (default ganeti))
  543. (port ganeti-metad-configuration-port ;integer
  544. (default 80))
  545. (address ganeti-metad-configuration-address ;string | #f
  546. (default #f))
  547. (debug? ganeti-metad-configuration-debug? ;Boolean
  548. (default #f)))
  549. (define ganeti-metad-service
  550. (match-lambda
  551. (($ <ganeti-metad-configuration> ganeti port address debug?)
  552. (list (shepherd-service
  553. (documentation "Run the Ganeti metadata daemon.")
  554. (provision '(ganeti-metad))
  555. (requirement '(user-processes networking))
  556. ;; This service is started on demand.
  557. (auto-start? #f)
  558. (respawn? #f)
  559. (start #~(make-forkexec-constructor
  560. (list #$(file-append ganeti "/sbin/ganeti-metad")
  561. #$(string-append "--port=" (number->string port))
  562. #$@(if address
  563. #~((string-append "--bind=" #$address))
  564. #~())
  565. #$@(if debug?
  566. #~("--debug")
  567. #~()))
  568. #:pid-file "/var/run/ganeti/ganeti-metad.pid"))
  569. (stop #~(make-kill-destructor)))))))
  570. (define ganeti-metad-service-type
  571. (service-type (name 'ganeti-metad)
  572. (extensions
  573. (list (service-extension shepherd-root-service-type
  574. ganeti-metad-service)))
  575. (default-value (ganeti-metad-configuration))
  576. (description
  577. "@command{ganeti-metad} is a daemon that can be used to pass
  578. information to OS install scripts or instances.")))
  579. (define-record-type* <ganeti-watcher-configuration>
  580. ganeti-watcher-configuration make-ganeti-watcher-configuration
  581. ganeti-watcher-configuration?
  582. (ganeti ganeti-watcher-configuration-ganeti ;file-like
  583. (default ganeti))
  584. (schedule ganeti-watcher-configuration-schedule ;list | string
  585. (default '(next-second-from
  586. ;; Run every five minutes.
  587. (next-minute (range 0 60 5)))))
  588. (rapi-ip ganeti-watcher-configuration-rapi-ip ;#f | string
  589. (default #f))
  590. (job-age ganeti-watcher-configuration-job-age ;integer
  591. (default (* 6 3600)))
  592. (verify-disks? ganeti-watcher-configuration-verify-disks? ;Boolean
  593. (default #t))
  594. (debug? ganeti-watcher-configuration-debug? ;Boolean
  595. (default #f)))
  596. (define ganeti-watcher-command
  597. (match-lambda
  598. (($ <ganeti-watcher-configuration> ganeti _ rapi-ip job-age verify-disks?
  599. debug?)
  600. #~(lambda ()
  601. (system* #$(file-append ganeti "/sbin/ganeti-watcher")
  602. #$@(if rapi-ip
  603. #~((string-append "--rapi-ip=" #$rapi-ip))
  604. #~())
  605. #$(string-append "--job-age=" (number->string job-age))
  606. #$@(if verify-disks?
  607. #~()
  608. #~("--no-verify-disks"))
  609. #$@(if debug?
  610. #~("--debug")
  611. #~()))))))
  612. (define (ganeti-watcher-jobs config)
  613. (match config
  614. (($ <ganeti-watcher-configuration> _ schedule)
  615. (list
  616. #~(job #$@(match schedule
  617. ((? string?)
  618. #~(#$schedule))
  619. ((? list?)
  620. #~('#$schedule)))
  621. #$(ganeti-watcher-command config)
  622. "ganeti-watcher")))))
  623. (define ganeti-watcher-service-type
  624. (service-type (name 'ganeti-watcher)
  625. (extensions
  626. (list (service-extension mcron-service-type
  627. ganeti-watcher-jobs)))
  628. (default-value (ganeti-watcher-configuration))
  629. (description
  630. "@command{ganeti-watcher} is a periodically run script that
  631. performs a number of maintenance actions on the cluster. It will automatically
  632. restart instances that are marked as ERROR_down, i.e., instances that should be
  633. running, but are not; and it will also try to repair DRBD links in case a
  634. secondary node has rebooted. In addition it is responsible for archiving old
  635. cluster jobs, and it will restart any down Ganeti daemons that are appropriate
  636. for the current node. If the cluster parameter @code{maintain_node_health} is
  637. enabled, the watcher will also shutdown instances and DRBD devices if the node
  638. is declared offline by known master candidates.")))
  639. (define-record-type* <ganeti-cleaner-configuration>
  640. ganeti-cleaner-configuration make-ganeti-cleaner-configuration
  641. ganeti-cleaner-configuration?
  642. (ganeti ganeti-cleaner-configuration-ganeti ;file-like
  643. (default ganeti))
  644. (master-schedule ganeti-cleaner-configuration-master-schedule ;list | string
  645. ;; Run the master cleaner at 01:45 every day.
  646. (default "45 1 * * *"))
  647. (node-schedule ganeti-cleaner-configuration-node-schedule ;list | string
  648. ;; Run the node cleaner at 02:45 every day.
  649. (default "45 2 * * *")))
  650. (define ganeti-cleaner-jobs
  651. (match-lambda
  652. (($ <ganeti-cleaner-configuration> ganeti master-schedule node-schedule)
  653. (list
  654. #~(job #$@(match master-schedule
  655. ((? string?)
  656. #~(#$master-schedule))
  657. ((? list?)
  658. #~('#$master-schedule)))
  659. (lambda ()
  660. (system* #$(file-append ganeti "/sbin/ganeti-cleaner")
  661. "master"))
  662. "ganeti master cleaner")
  663. #~(job #$@(match node-schedule
  664. ((? string?)
  665. #~(#$node-schedule))
  666. ((? list?)
  667. #~('#$node-schedule)))
  668. (lambda ()
  669. (system* #$(file-append ganeti "/sbin/ganeti-cleaner")
  670. "node"))
  671. "ganeti node cleaner")))))
  672. (define ganeti-cleaner-service-type
  673. (service-type (name 'ganeti-cleaner)
  674. (extensions
  675. (list (service-extension mcron-service-type
  676. ganeti-cleaner-jobs)))
  677. (default-value (ganeti-cleaner-configuration))
  678. (description
  679. "@command{ganeti-cleaner} is a script that removes old files
  680. from the cluster. When called with @code{node} as argument it removes expired
  681. X509 certificates and keys from @file{/var/run/ganeti/crypto}, as well as
  682. outdated @command{ganeti-watcher} information.
  683. When called with @code{master} as argument, it instead removes files older
  684. than 21 days from @file{/var/lib/ganeti/queue/archive}.")))
  685. (define-record-type* <ganeti-configuration>
  686. ganeti-configuration make-ganeti-configuration
  687. ganeti-configuration?
  688. (ganeti ganeti-configuration-ganeti
  689. (default ganeti))
  690. (noded-configuration ganeti-configuration-noded-configuration
  691. (default (ganeti-noded-configuration)))
  692. (confd-configuration ganeti-configuration-confd-configuration
  693. (default (ganeti-confd-configuration)))
  694. (wconfd-configuration ganeti-configuration-wconfd-configuration
  695. (default (ganeti-wconfd-configuration)))
  696. (luxid-configuration ganeti-configuration-luxid-configuration
  697. (default (ganeti-luxid-configuration)))
  698. (rapi-configuration ganeti-configuration-rapi-configuration
  699. (default (ganeti-rapi-configuration)))
  700. (kvmd-configuration ganeti-configuration-kvmd-configuration
  701. (default (ganeti-kvmd-configuration)))
  702. (mond-configuration ganeti-configuration-mond-configuration
  703. (default (ganeti-mond-configuration)))
  704. (metad-configuration ganeti-configuration-metad-configuration
  705. (default (ganeti-metad-configuration)))
  706. (watcher-configuration ganeti-configuration-watcher-configuration
  707. (default (ganeti-watcher-configuration)))
  708. (cleaner-configuration ganeti-configuration-cleaner-configuration
  709. (default (ganeti-cleaner-configuration)))
  710. (file-storage-paths ganeti-configuration-file-storage-paths ;list of strings | gexp
  711. (default '()))
  712. (hooks ganeti-configuration-hooks ;<file-like> | #f
  713. (default #f))
  714. (os ganeti-configuration-os ;list of <ganeti-os>
  715. (default '())))
  716. (define (ganeti-activation config)
  717. (with-imported-modules '((guix build utils))
  718. #~(begin
  719. (use-modules (guix build utils))
  720. (for-each mkdir-p
  721. '("/var/log/ganeti"
  722. "/var/log/ganeti/kvm"
  723. "/var/log/ganeti/os"
  724. "/var/lib/ganeti/rapi"
  725. "/var/lib/ganeti/queue"
  726. "/var/lib/ganeti/queue/archive"
  727. "/var/run/ganeti/bdev-cache"
  728. "/var/run/ganeti/crypto"
  729. "/var/run/ganeti/socket"
  730. "/var/run/ganeti/instance-disks"
  731. "/var/run/ganeti/instance-reason"
  732. "/var/run/ganeti/livelocks")))))
  733. (define ganeti-shepherd-services
  734. (match-lambda
  735. (($ <ganeti-configuration> _ noded confd wconfd luxid rapi kvmd mond metad)
  736. (append (ganeti-noded-service noded)
  737. (ganeti-confd-service confd)
  738. (ganeti-wconfd-service wconfd)
  739. (ganeti-luxid-service luxid)
  740. (ganeti-rapi-service rapi)
  741. (ganeti-kvmd-service kvmd)
  742. (ganeti-mond-service mond)
  743. (ganeti-metad-service metad)))))
  744. (define ganeti-mcron-jobs
  745. (match-lambda
  746. (($ <ganeti-configuration> _ _ _ _ _ _ _ _ _ watcher cleaner)
  747. (append (ganeti-watcher-jobs watcher)
  748. (ganeti-cleaner-jobs cleaner)))))
  749. (define-record-type* <ganeti-os>
  750. ganeti-os make-ganeti-os ganeti-os?
  751. (name ganeti-os-name) ;string
  752. (extension ganeti-os-extension ;#f | string
  753. (default #f))
  754. (variants ganeti-os-variants ;<file-like> | list of <ganeti-os-variant>
  755. (default '())))
  756. (define-record-type* <ganeti-os-variant>
  757. ganeti-os-variant make-ganeti-os-variant ganeti-os-variant?
  758. (name ganeti-os-variant-name) ;string
  759. (configuration ganeti-os-variant-configuration)) ;<file-like>
  760. (define %debootstrap-interfaces-hook
  761. (file-append ganeti-instance-debootstrap
  762. "/share/doc/ganeti-instance-debootstrap/examples/interfaces"))
  763. ;; The GRUB hook shipped with instance-debootstrap does not work with GRUB2.
  764. ;; For convenience, provide one that work with modern Debians here.
  765. ;; Note: it would be neat to reuse Guix' bootloader infrastructure instead.
  766. (define %debootstrap-grub-hook
  767. (plain-file "grub"
  768. "#!/usr/bin/env bash
  769. CLEANUP=( )
  770. cleanup() {
  771. if [ ${#CLEANUP[*]} -gt 0 ]; then
  772. LAST_ELEMENT=$((${#CLEANUP[*]}-1))
  773. REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
  774. for i in $REVERSE_INDEXES; do
  775. ${CLEANUP[$i]}
  776. done
  777. fi
  778. }
  779. trap cleanup EXIT
  780. mount -t proc proc $TARGET/proc
  781. CLEANUP+=(\"umount $TARGET/proc\")
  782. mount -t sysfs sysfs $TARGET/sys
  783. CLEANUP+=(\"umount $TARGET/sys\")
  784. mount -o bind /dev $TARGET/dev
  785. CLEANUP+=(\"umount $TARGET/dev\")
  786. echo '
  787. GRUB_TIMEOUT_STYLE=menu
  788. GRUB_CMDLINE_LINUX_DEFAULT=\"console=ttyS0,115200 net.ifnames=0\"
  789. GRUB_TERMINAL=\"serial\"
  790. GRUB_SERIAL_COMMAND=\"serial --unit=0 --speed=115200\"
  791. ' >> $TARGET/etc/default/grub
  792. # This PATH is propagated into the chroot and necessary to make grub-install
  793. # and related commands visible.
  794. export PATH=\"/usr/sbin:/usr/bin:/sbin:/bin:$PATH\"
  795. chroot \"$TARGET\" grub-install $BLOCKDEV
  796. chroot \"$TARGET\" update-grub
  797. cleanup
  798. trap - EXIT
  799. "))
  800. (define %default-debootstrap-hooks
  801. `((10-interfaces . ,%debootstrap-interfaces-hook)
  802. (90-grub . ,%debootstrap-grub-hook)))
  803. (define %default-debootstrap-extra-pkgs
  804. ;; Packages suitable for a fully virtualized KVM guest.
  805. '("acpi-support-base" "udev" "linux-image-amd64" "openssh-server"
  806. "locales-all" "grub-pc"))
  807. (define-record-type* <debootstrap-configuration>
  808. debootstrap-configuration make-debootstrap-configuration
  809. debootstrap-configuration?
  810. (hooks debootstrap-configuration-hooks ;#f | gexp | '((name . gexp))
  811. (default %default-debootstrap-hooks))
  812. (proxy debootstrap-configuration-proxy (default #f)) ;#f | string
  813. (mirror debootstrap-configuration-mirror ;#f | string
  814. (default #f))
  815. (arch debootstrap-configuration-arch (default #f)) ;#f | string
  816. (suite debootstrap-configuration-suite ;#f | string
  817. (default "stable"))
  818. (extra-pkgs debootstrap-configuration-extra-pkgs ;list of strings
  819. (default %default-debootstrap-extra-pkgs))
  820. (components debootstrap-configuration-components ;list of strings
  821. (default '()))
  822. (generate-cache? debootstrap-configuration-generate-cache? ;Boolean
  823. (default #t))
  824. (clean-cache debootstrap-configuration-clean-cache ;#f | integer
  825. (default 14))
  826. (partition-style debootstrap-configuration-partition-style ;#f | symbol | string
  827. (default 'msdos))
  828. (partition-alignment debootstrap-configuration-partition-alignment ;#f | integer
  829. (default 2048)))
  830. (define (debootstrap-hooks->directory hooks)
  831. (match hooks
  832. ((? file-like?)
  833. hooks)
  834. ((? list?)
  835. (let ((names (map car hooks))
  836. (files (map cdr hooks)))
  837. (with-imported-modules '((guix build utils))
  838. (computed-file "debootstrap-hooks"
  839. #~(begin
  840. (use-modules (guix build utils)
  841. (ice-9 match))
  842. (mkdir-p #$output)
  843. (with-directory-excursion #$output
  844. (for-each (match-lambda
  845. ((name hook)
  846. (let ((file-name (string-append
  847. #$output "/"
  848. (symbol->string name))))
  849. ;; Copy to the destination to ensure
  850. ;; the file is executable.
  851. (copy-file hook file-name)
  852. (chmod file-name #o555))))
  853. '#$(zip names files))))))))
  854. (_ #f)))
  855. (define-gexp-compiler (debootstrap-configuration-compiler
  856. (file <debootstrap-configuration>) system target)
  857. (match file
  858. (($ <debootstrap-configuration> hooks proxy mirror arch suite extra-pkgs
  859. components generate-cache? clean-cache
  860. partition-style partition-alignment)
  861. (let ((customize-dir (debootstrap-hooks->directory hooks)))
  862. (gexp->derivation
  863. "debootstrap-variant"
  864. #~(call-with-output-file (ungexp output "out")
  865. (lambda (port)
  866. (display
  867. (string-append
  868. (ungexp-splicing
  869. `(,@(if proxy
  870. `("PROXY=" ,proxy "\n")
  871. '())
  872. ,@(if mirror
  873. `("MIRROR=" ,mirror "\n")
  874. '())
  875. ,@(if arch
  876. `("ARCH=" ,arch "\n")
  877. '())
  878. ,@(if suite
  879. `("SUITE=" ,suite "\n")
  880. '())
  881. ,@(if (not (null? extra-pkgs))
  882. `("EXTRA_PKGS=" ,(string-join extra-pkgs ",") "\n")
  883. '())
  884. ,@(if (not (null? components))
  885. `("COMPONENTS=" ,(string-join components ",") "\n")
  886. '())
  887. ,@(if customize-dir
  888. `("CUSTOMIZE_DIR=" ,customize-dir "\n")
  889. '())
  890. ,@(if generate-cache?
  891. '("GENERATE_CACHE=yes\n")
  892. '("GENERATE_CACHE=no\n"))
  893. ,@(if clean-cache
  894. `("CLEAN_CACHE=" ,(number->string clean-cache) "\n")
  895. '())
  896. ,@(if partition-style
  897. (if (symbol? partition-style)
  898. `("PARTITION_STYLE="
  899. ,(symbol->string partition-style) "\n")
  900. `("PARTITION_STYLE=" ,partition-style "\n"))
  901. '())
  902. ,@(if partition-alignment
  903. `("PARTITION_ALIGNMENT="
  904. ,(number->string partition-alignment) "\n")
  905. '()))))
  906. port)))
  907. #:local-build? #t)))))
  908. (define (ganeti-os->directory os)
  909. "Return the derivation to build the configuration directory to be installed
  910. in /etc/ganeti/instance-$os for OS."
  911. (let ((name (ganeti-os-name os))
  912. (extension (ganeti-os-extension os))
  913. (variants (ganeti-os-variants os)))
  914. (define builder
  915. (with-imported-modules '((guix build utils))
  916. (if (file-like? variants)
  917. #~(begin
  918. (use-modules (guix build utils))
  919. (mkdir-p #$output)
  920. (symlink #$variants
  921. (string-append #$output "/variants")))
  922. #~(begin
  923. (use-modules (guix build utils)
  924. (ice-9 format)
  925. (ice-9 match)
  926. (srfi srfi-1))
  927. (mkdir-p #$output)
  928. (let ((variants-dir (string-append #$output "/variants"))
  929. (names '#$(map ganeti-os-variant-name variants))
  930. (configs '#$(map ganeti-os-variant-configuration variants)))
  931. (mkdir-p variants-dir)
  932. (unless (null? names)
  933. (call-with-output-file (string-append variants-dir
  934. "/variants.list")
  935. (lambda (port)
  936. (format port "~a~%"
  937. (string-join names "\n"))))
  938. (for-each (match-lambda
  939. ((name file)
  940. (let ((file-name
  941. (if #$extension
  942. (string-append name #$extension)
  943. name)))
  944. (symlink file
  945. (string-append variants-dir "/"
  946. file-name)))))
  947. (zip names configs))))))))
  948. (computed-file (string-append name "-os") builder
  949. #:local-build? #t)))
  950. (define (ganeti-directory file-storage-file hooks os)
  951. (let ((dirs (map ganeti-os->directory os))
  952. (names (map ganeti-os-name os)))
  953. (define builder
  954. #~(begin
  955. (use-modules (ice-9 match))
  956. (mkdir #$output)
  957. (when #$file-storage-file
  958. (symlink #$file-storage-file
  959. (string-append #$output "/file-storage-paths")))
  960. (when #$hooks
  961. (symlink #$hooks
  962. (string-append #$output "/hooks")))
  963. (for-each (match-lambda
  964. ((name dest)
  965. (symlink dest
  966. (string-append #$output "/instance-" name))))
  967. '#$(zip names dirs))))
  968. (computed-file "etc-ganeti" builder)))
  969. (define (file-storage-file paths)
  970. (match paths
  971. ((? null?) #f)
  972. ((? list?) (plain-file
  973. "file-storage-paths"
  974. (string-join paths "\n")))
  975. (_ paths)))
  976. (define (ganeti-etc-service config)
  977. (list `("ganeti" ,(ganeti-directory
  978. (file-storage-file
  979. (ganeti-configuration-file-storage-paths config))
  980. (ganeti-configuration-hooks config)
  981. (ganeti-configuration-os config)))))
  982. (define (debootstrap-os variants)
  983. (ganeti-os
  984. (name "debootstrap")
  985. (extension ".conf")
  986. (variants variants)))
  987. (define (debootstrap-variant name configuration)
  988. (ganeti-os-variant
  989. (name name)
  990. (configuration configuration)))
  991. (define %default-debootstrap-variants
  992. (list (debootstrap-variant
  993. "default"
  994. (debootstrap-configuration))))
  995. (define (guix-os variants)
  996. (ganeti-os
  997. (name "guix")
  998. (extension ".scm")
  999. (variants variants)))
  1000. (define (guix-variant name configuration)
  1001. (ganeti-os-variant
  1002. (name name)
  1003. (configuration configuration)))
  1004. (define %default-guix-variants
  1005. (list (guix-variant
  1006. "default"
  1007. (file-append ganeti-instance-guix
  1008. "/share/doc/ganeti-instance-guix/examples/dynamic.scm"))))
  1009. ;; The OS configurations usually come with a default OS. To make them work
  1010. ;; out of the box, follow suit.
  1011. (define %default-ganeti-os
  1012. (list (debootstrap-os %default-debootstrap-variants)
  1013. (guix-os %default-guix-variants)))
  1014. (define ganeti-service-type
  1015. (service-type (name 'ganeti)
  1016. (extensions
  1017. (list (service-extension activation-service-type
  1018. ganeti-activation)
  1019. (service-extension shepherd-root-service-type
  1020. ganeti-shepherd-services)
  1021. (service-extension etc-service-type
  1022. ganeti-etc-service)
  1023. (service-extension profile-service-type
  1024. (compose list ganeti-configuration-ganeti))
  1025. (service-extension mcron-service-type
  1026. ganeti-mcron-jobs)))
  1027. (default-value (ganeti-configuration (os %default-ganeti-os)))
  1028. (description
  1029. "Ganeti is a family of services that are designed to run
  1030. on a fleet of machines and facilitate deployment and maintenance of virtual
  1031. servers (@dfn{instances}). It can migrate instances between nodes, automatically
  1032. restart failed instances, evacuate nodes, and much more.")))