projects/guix·blob python-team · refs

1125 lines · 48.2 KiB

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