1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
4 ;;; This file is part of GNU Guix.
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.
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.
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/>.
19 (define-module (gnu services ganeti)
20 #:use-module (gnu packages virtualization)
21 #:use-module (gnu services)
22 #:use-module (gnu services mcron)
23 #:use-module (gnu services shepherd)
24 #:use-module (guix gexp)
25 #:use-module (guix records)
27 #:use-module (srfi srfi-1)
28 #:use-module (ice-9 match)
30 #:export (ganeti-noded-configuration
31 ganeti-noded-configuration?
32 ganeti-noded-configuration-ganeti
33 ganeti-noded-configuration-port
34 ganeti-noded-configuration-address
35 ganeti-noded-configuration-interface
36 ganeti-noded-configuration-max-clients
37 ganeti-noded-configuration-ssl?
38 ganeti-noded-configuration-ssl-key
39 ganeti-noded-configuration-ssl-cert
40 ganeti-noded-configuration-debug?
41 ganeti-noded-service-type
43 ganeti-confd-configuration
44 ganeti-confd-configuration?
45 ganeti-confd-configuration-ganeti
46 ganeti-confd-configuration-port
47 ganeti-confd-configuration-address
48 ganeti-confd-configuration-debug
49 ganeti-confd-service-type
51 ganeti-wconfd-configuration
52 ganeti-wconfd-configuration?
53 ganeti-wconfd-configuration-ganeti
54 ganeti-wconfd-configuration-no-voting?
55 ganeti-wconfd-configuration-debug?
56 ganeti-wconfd-service-type
58 ganeti-luxid-configuration
59 ganeti-luxid-configuration?
60 ganeti-luxid-configuration-ganeti
61 ganeti-luxid-configuration-no-voting?
62 ganeti-luxid-configuration-debug?
63 ganeti-luxid-service-type
65 ganeti-rapi-configuration
66 ganeti-rapi-configuration?
67 ganeti-rapi-configuration-ganeti
68 ganeti-rapi-configuration-require-authentication?
69 ganeti-rapi-configuration-port
70 ganeti-rapi-configuration-address
71 ganeti-rapi-configuration-interface
72 ganeti-rapi-configuration-max-clients
73 ganeti-rapi-configuration-ssl?
74 ganeti-rapi-configuration-ssl-key
75 ganeti-rapi-configuration-ssl-cert
76 ganeti-rapi-configuration-debug?
77 ganeti-rapi-service-type
79 ganeti-kvmd-configuration
80 ganeti-kvmd-configuration?
81 ganeti-kvmd-configuration-ganeti
82 ganeti-kvmd-configuration-debug?
83 ganeti-kvmd-service-type
85 ganeti-mond-configuration
86 ganeti-mond-configuration?
87 ganeti-mond-configuration-ganeti
88 ganeti-mond-configuration-port
89 ganeti-mond-configuration-address
90 ganeti-mond-configuration-debug?
91 ganeti-mond-service-type
93 ganeti-metad-configuration
94 ganeti-metad-configuration?
95 ganeti-metad-configuration-ganeti
96 ganeti-metad-configuration-port
97 ganeti-metad-configuration-address
98 ganeti-metad-configuration-debug?
99 ganeti-metad-service-type
101 ganeti-watcher-configuration
102 ganeti-watcher-configuration?
103 ganeti-watcher-configuration-ganeti
104 ganeti-watcher-configuration-schedule
105 ganeti-watcher-configuration-rapi-ip
106 ganeti-watcher-configuration-job-age
107 ganeti-watcher-configuration-verify-disks?
108 ganeti-watcher-configuration-debug?
109 ganeti-watcher-service-type
111 ganeti-cleaner-configuration
112 ganeti-cleaner-configuration?
113 ganeti-cleaner-configuration-ganeti
114 ganeti-cleaner-configuration-master-schedule
115 ganeti-cleaner-configuration-node-schedule
116 ganeti-cleaner-service-type
126 ganeti-os-variant-name
127 ganeti-os-variant-configuration
129 %debootstrap-interfaces-hook
130 %debootstrap-grub-hook
131 %default-debootstrap-hooks
132 %default-debootstrap-extra-pkgs
133 debootstrap-configuration
134 debootstrap-configuration?
135 debootstrap-configuration-hooks
136 debootstrap-configuration-proxy
137 debootstrap-configuration-mirror
138 debootstrap-configuration-arch
139 debootstrap-configuration-suite
140 debootstrap-configuration-extra-pkgs
141 debootstrap-configuration-components
142 debootstrap-configuration-generate-cache?
143 debootstrap-configuration-clean-cache
144 debootstrap-configuration-partition-style
145 debootstrap-configuration-partition-alignment
149 %default-debootstrap-variants
153 %default-guix-variants
158 ganeti-configuration?
159 ganeti-configuration-noded-configuration
160 ganeti-configuration-confd-configuration
161 ganeti-configuration-wconfd-configuration
162 ganeti-configuration-luxid-configuration
163 ganeti-configuration-rapi-configuration
164 ganeti-configuration-kvmd-configuration
165 ganeti-configuration-mond-configuration
166 ganeti-configuration-metad-configuration
167 ganeti-configuration-watcher-configuration
168 ganeti-configuration-cleaner-configuration
169 ganeti-configuration-file-storage-paths
170 ganeti-configuration-os
171 ganeti-service-type))
174 ;;; Service definitions for running a Ganeti cluster.
176 ;;; Planned improvements: run daemons (except ganeti-noded) under unprivileged
177 ;;; user accounts and/or containers. The account names must match the ones
178 ;;; given to Ganetis configure script. metad needs "setcap" or root in order
179 ;;; to bind on port 80.
181 ;; Set PATH so the various daemons are able to find the 'ip' executable, LVM,
182 ;; Ceph, Gluster, etc, without having to add absolute references to everything.
183 (define %default-ganeti-environment-variables
184 (list (string-append "PATH="
185 (string-join '("/run/setuid-programs"
186 "/run/current-system/profile/sbin"
187 "/run/current-system/profile/bin")
190 (define-record-type* <ganeti-noded-configuration>
191 ganeti-noded-configuration make-ganeti-noded-configuration
192 ganeti-noded-configuration?
193 (ganeti ganeti-noded-configuration-ganeti ;<package>
195 (port ganeti-noded-configuration-port ;integer
197 (address ganeti-noded-configuration-address ;string
199 (interface ganeti-noded-configuration-interface ;string | #f
201 (max-clients ganeti-noded-configuration-max-clients ;integer
203 (ssl? ganeti-noded-configuration-ssl? ;Boolean
205 (ssl-key ganeti-noded-configuration-ssl-key ;string
206 (default "/var/lib/ganeti/server.pem"))
207 (ssl-cert ganeti-noded-configuration-ssl-cert ;string
208 (default "/var/lib/ganeti/server.pem"))
209 (debug? ganeti-noded-configuration-debug? ;Boolean
212 (define ganeti-noded-service
214 (($ <ganeti-noded-configuration> ganeti port address interface max-clients
215 ssl? ssl-key ssl-cert debug?)
216 (list (shepherd-service
217 (documentation "Run the Ganeti node daemon.")
218 (provision '(ganeti-noded))
219 (requirement '(user-processes networking))
221 ;; If the daemon stops, it is probably for a good reason;
222 ;; otherwise ganeti-watcher will restart it for us anyway.
225 (start #~(make-forkexec-constructor
226 (list #$(file-append ganeti "/sbin/ganeti-noded")
227 #$(string-append "--port=" (number->string port))
228 #$(string-append "--bind=" address)
230 #~((string-append "--interface=" #$interface))
232 #$(string-append "--max-clients="
233 (number->string max-clients))
235 #~((string-append "--ssl-key=" #$ssl-key)
236 (string-append "--ssl-cert=" #$ssl-cert))
241 #:environment-variables
242 '#$%default-ganeti-environment-variables
243 #:pid-file "/var/run/ganeti/ganeti-noded.pid"))
244 (stop #~(make-kill-destructor)))))))
246 (define ganeti-noded-service-type
247 (service-type (name 'ganeti-noded)
249 (list (service-extension shepherd-root-service-type
250 ganeti-noded-service)))
251 (default-value (ganeti-noded-configuration))
253 "@command{ganeti-noded} is the daemon which is responsible
254 for the node functions in the Ganeti system.")))
256 (define-record-type* <ganeti-confd-configuration>
257 ganeti-confd-configuration make-ganeti-confd-configuration
258 ganeti-confd-configuration?
259 (ganeti ganeti-confd-configuration-ganeti ;<package>
261 (port ganeti-confd-configuration-port ;integer
263 (address ganeti-confd-configuration-address ;string
265 (debug? ganeti-confd-configuration-debug? ;Boolean
268 (define ganeti-confd-service
270 (($ <ganeti-confd-configuration> ganeti port address debug?)
271 (list (shepherd-service
272 (documentation "Run the Ganeti confd daemon.")
273 (provision '(ganeti-confd))
274 (requirement '(user-processes networking))
276 (start #~(make-forkexec-constructor
277 (list #$(file-append ganeti "/sbin/ganeti-confd")
278 #$(string-append "--port=" (number->string port))
279 #$(string-append "--bind=" address)
283 #:environment-variables
284 '#$%default-ganeti-environment-variables
285 #:pid-file "/var/run/ganeti/ganeti-confd.pid"))
286 (stop #~(make-kill-destructor)))))))
288 (define ganeti-confd-service-type
289 (service-type (name 'ganeti-confd)
291 (list (service-extension shepherd-root-service-type
292 ganeti-confd-service)))
293 (default-value (ganeti-confd-configuration))
295 "@command{ganeti-confd} is a daemon used to answer queries
296 related to the configuration of a Ganeti cluster.")))
298 (define-record-type* <ganeti-wconfd-configuration>
299 ganeti-wconfd-configuration make-ganeti-wconfd-configuration
300 ganeti-wconfd-configuration?
301 (ganeti ganeti-wconfd-configuration-ganeti ;<package>
303 (no-voting? ganeti-wconfd-configuration-no-voting? ;Boolean
305 (debug? ganeti-wconfd-configuration-debug? ;Boolean
308 ;; If this file exists, the wconfd daemon will be forcefully started even on
309 ;; non-master nodes. It is used to accommodate a master-failover scenario.
310 (define %wconfd-force-node-hint
311 "/var/lib/ganeti/guix_wconfd_force_node_hint")
313 (define (wconfd-wrapper ganeti args)
314 ;; Wrapper for the wconfd daemon that looks for the force-node hint.
318 (let ((wconfd #$(file-append ganeti "/sbin/ganeti-wconfd"))
319 (force-node? (file-exists? #$%wconfd-force-node-hint)))
321 (execl wconfd wconfd "--force-node" "--no-voting" "--yes-do-it" #$@args)
322 (execl wconfd wconfd #$@args))))))
324 (define shepherd-wconfd-force-start-action
325 ;; Shepherd action to create the force-node hint and start wconfd.
329 "Forcefully start wconfd even on non-master nodes (dangerous!).")
330 (procedure #~(lambda _
331 (format #t "Forcefully starting the wconfd daemon...~%")
332 (action 'ganeti-wconfd 'enable)
336 (call-with-output-file #$%wconfd-force-node-hint
340 (action 'ganeti-wconfd 'restart))
342 (delete-file #$%wconfd-force-node-hint)))
345 (define ganeti-wconfd-service
347 (($ <ganeti-wconfd-configuration> ganeti no-voting? debug?)
348 (list (shepherd-service
349 (documentation "Run the Ganeti wconfd daemon.")
350 (provision '(ganeti-wconfd))
351 (requirement '(user-processes))
353 ;; Shepherd action to support a master-failover scenario. It is
354 ;; automatically invoked during 'gnt-cluster master-failover' (see
355 ;; related Ganeti patch) and not intended for interactive use.
356 (actions (list shepherd-wconfd-force-start-action))
358 ;; wconfd will disable itself when not running on the master
359 ;; node. Don't attempt to restart it.
363 #~(make-forkexec-constructor
364 (list #$(wconfd-wrapper ganeti
367 '("--no-voting" "--yes-do-it")
372 #:environment-variables
373 '#$%default-ganeti-environment-variables
374 #:pid-file "/var/run/ganeti/ganeti-wconfd.pid"))
375 (stop #~(make-kill-destructor)))))))
377 (define ganeti-wconfd-service-type
378 (service-type (name 'ganeti-wconfd)
380 (list (service-extension shepherd-root-service-type
381 ganeti-wconfd-service)))
382 (default-value (ganeti-wconfd-configuration))
384 "@command{ganeti-wconfd} is the daemon that has authoritative
385 knowledge about the configuration and is the only entity that can accept changes
386 to it. All jobs that need to modify the configuration will do so by sending
387 appropriate requests to this daemon.")))
389 (define-record-type* <ganeti-luxid-configuration>
390 ganeti-luxid-configuration make-ganeti-luxid-configuration
391 ganeti-luxid-configuration?
392 (ganeti ganeti-luxid-configuration-ganeti ;<package>
394 (no-voting? ganeti-luxid-configuration-no-voting? ;Boolean
396 (debug? ganeti-luxid-configuration-debug? ;Boolean
399 (define ganeti-luxid-service
401 (($ <ganeti-luxid-configuration> ganeti no-voting? debug?)
402 (list (shepherd-service
403 (documentation "Run the Ganeti LUXI daemon.")
404 (provision '(ganeti-luxid))
405 (requirement '(user-processes))
407 ;; This service will automatically disable itself when not
408 ;; running on the master node. Don't attempt to restart it.
411 (start #~(make-forkexec-constructor
412 (list #$(file-append ganeti "/sbin/ganeti-luxid")
414 #~("--no-voting" "--yes-do-it")
419 #:environment-variables
420 '#$%default-ganeti-environment-variables
421 #:pid-file "/var/run/ganeti/ganeti-luxid.pid"))
422 (stop #~(make-kill-destructor)))))))
424 (define ganeti-luxid-service-type
425 (service-type (name 'ganeti-luxid)
427 (list (service-extension shepherd-root-service-type
428 ganeti-luxid-service)))
429 (default-value (ganeti-luxid-configuration))
431 "@command{ganeti-luxid} is a daemon used to answer queries
432 related to the configuration and the current live state of a Ganeti cluster.
433 Additionally, it is the authoritative daemon for the Ganeti job queue. Jobs can
434 be submitted via this daemon and it schedules and starts them.")))
436 (define-record-type* <ganeti-rapi-configuration>
437 ganeti-rapi-configuration make-ganeti-rapi-configuration
438 ganeti-rapi-configuration?
439 (ganeti ganeti-rapi-configuration-ganeti ;<package>
441 (require-authentication?
442 ganeti-rapi-configuration-require-authentication? ;Boolean
444 (port ganeti-rapi-configuration-port ;integer
446 (address ganeti-rapi-configuration-address ;string
448 (interface ganeti-rapi-configuration-interface ;string | #f
450 (max-clients ganeti-rapi-configuration-max-clients ;integer
452 (ssl? ganeti-rapi-configuration-ssl? ;Boolean
454 (ssl-key ganeti-rapi-configuration-ssl-key ;string
455 (default "/var/lib/ganeti/server.pem"))
456 (ssl-cert ganeti-rapi-configuration-ssl-cert ;string
457 (default "/var/lib/ganeti/server.pem"))
458 (debug? ganeti-rapi-configuration-debug? ;Boolean
461 (define ganeti-rapi-service
463 (($ <ganeti-rapi-configuration> ganeti require-authentication? port address
464 interface max-clients ssl? ssl-key ssl-cert
466 (list (shepherd-service
467 (documentation "Run the Ganeti RAPI daemon.")
468 (provision '(ganeti-rapi))
469 (requirement '(user-processes networking))
471 ;; This service will automatically disable itself when not
472 ;; running on the master node. Don't attempt to restart it.
475 (start #~(make-forkexec-constructor
476 (list #$(file-append ganeti "/sbin/ganeti-rapi")
477 #$@(if require-authentication?
478 #~("--require-authentication")
480 #$(string-append "--port=" (number->string port))
481 #$(string-append "--bind=" address)
483 #~((string-append "--interface=" #$interface))
485 #$(string-append "--max-clients="
486 (number->string max-clients))
488 #~((string-append "--ssl-key=" #$ssl-key)
489 (string-append "--ssl-cert=" #$ssl-cert))
494 #:environment-variables
495 '#$%default-ganeti-environment-variables
496 #:pid-file "/var/run/ganeti/ganeti-rapi.pid"))
497 (stop #~(make-kill-destructor)))))))
499 (define ganeti-rapi-service-type
500 (service-type (name 'ganeti-rapi)
502 (list (service-extension shepherd-root-service-type
503 ganeti-rapi-service)))
504 (default-value (ganeti-rapi-configuration))
506 "@command{ganeti-rapi} is the daemon providing a remote API
507 for Ganeti clusters.")))
509 (define-record-type* <ganeti-kvmd-configuration>
510 ganeti-kvmd-configuration make-ganeti-kvmd-configuration
511 ganeti-kvmd-configuration?
512 (ganeti ganeti-kvmd-configuration-ganeti ;<package>
514 (debug? ganeti-kvmd-configuration-debug? ;Boolean
517 (define ganeti-kvmd-service
519 (($ <ganeti-kvmd-configuration> ganeti debug?)
520 (list (shepherd-service
521 (documentation "Run the Ganeti KVM daemon.")
522 (provision '(ganeti-kvmd))
523 (requirement '(user-processes))
525 ;; This service will automatically disable itself when not
526 ;; needed. Don't attempt to restart it.
529 (start #~(make-forkexec-constructor
530 (list #$(file-append ganeti "/sbin/ganeti-kvmd")
534 #:environment-variables
535 '#$%default-ganeti-environment-variables
536 #:pid-file "/var/run/ganeti/ganeti-kvmd.pid"))
537 (stop #~(make-kill-destructor)))))))
539 (define ganeti-kvmd-service-type
540 (service-type (name 'ganeti-kvmd)
542 (list (service-extension shepherd-root-service-type
543 ganeti-kvmd-service)))
544 (default-value (ganeti-kvmd-configuration))
546 "@command{ganeti-kvmd} is responsible for determining whether
547 a given KVM instance was shutdown by an administrator or a user.
549 The KVM daemon monitors, using @code{inotify}, KVM instances through their QMP
550 sockets, which are provided by KVM. Using the QMP sockets, the KVM daemon
551 listens for particular shutdown, powerdown, and stop events which will determine
552 if a given instance was shutdown by the user or Ganeti, and this result is
553 communicated to Ganeti via a special file in the file system.")))
555 (define-record-type* <ganeti-mond-configuration>
556 ganeti-mond-configuration make-ganeti-mond-configuration
557 ganeti-mond-configuration?
558 (ganeti ganeti-mond-configuration-ganeti ;<package>
560 (port ganeti-mond-configuration-port ;integer
562 (address ganeti-mond-configuration-address ;string
564 (debug? ganeti-mond-configuration-debug? ;Boolean
567 (define ganeti-mond-service
569 (($ <ganeti-mond-configuration> ganeti port address debug?)
570 (list (shepherd-service
571 (documentation "Run the Ganeti monitoring daemon.")
572 (provision '(ganeti-mond))
573 (requirement '(user-processes networking))
575 (start #~(make-forkexec-constructor
576 (list #$(file-append ganeti "/sbin/ganeti-mond")
577 #$(string-append "--port=" (number->string port))
578 #$(string-append "--bind=" address)
582 #:pid-file "/var/run/ganeti/ganeti-mond.pid"))
583 (stop #~(make-kill-destructor)))))))
585 (define ganeti-mond-service-type
586 (service-type (name 'ganeti-mond)
588 (list (service-extension shepherd-root-service-type
589 ganeti-mond-service)))
590 (default-value (ganeti-mond-configuration))
592 "@command{ganeti-mond} is a daemon providing monitoring
593 functionality. It is responsible for running the data collectors and to
594 provide the collected information through a HTTP interface.")))
596 (define-record-type* <ganeti-metad-configuration>
597 ganeti-metad-configuration make-ganeti-metad-configuration
598 ganeti-metad-configuration?
599 (ganeti ganeti-metad-configuration-ganeti ;<package>
601 (port ganeti-metad-configuration-port ;integer
603 (address ganeti-metad-configuration-address ;string | #f
605 (debug? ganeti-metad-configuration-debug? ;Boolean
608 (define ganeti-metad-service
610 (($ <ganeti-metad-configuration> ganeti port address debug?)
611 (list (shepherd-service
612 (documentation "Run the Ganeti metadata daemon.")
613 (provision '(ganeti-metad))
614 (requirement '(user-processes networking))
616 (start #~(make-forkexec-constructor
617 (list #$(file-append ganeti "/sbin/ganeti-metad")
618 #$(string-append "--port=" (number->string port))
620 #~((string-append "--bind=" #$address))
625 #:pid-file "/var/run/ganeti/ganeti-metad.pid"))
626 (stop #~(make-kill-destructor)))))))
628 (define ganeti-metad-service-type
629 (service-type (name 'ganeti-metad)
631 (list (service-extension shepherd-root-service-type
632 ganeti-metad-service)))
633 (default-value (ganeti-metad-configuration))
635 "@command{ganeti-metad} is a daemon that can be used to pass
636 information to OS install scripts or instances.")))
638 (define-record-type* <ganeti-watcher-configuration>
639 ganeti-watcher-configuration make-ganeti-watcher-configuration
640 ganeti-watcher-configuration?
641 (ganeti ganeti-watcher-configuration-ganeti ;<package>
643 (schedule ganeti-watcher-configuration-schedule ;list | string
644 (default '(next-second-from
645 ;; Run every five minutes.
646 (next-minute (range 0 60 5)))))
647 (rapi-ip ganeti-watcher-configuration-rapi-ip ;#f | string
649 (job-age ganeti-watcher-configuration-job-age ;integer
650 (default (* 6 3600)))
651 (verify-disks? ganeti-watcher-configuration-verify-disks? ;Boolean
653 (debug? ganeti-watcher-configuration-debug? ;Boolean
656 (define ganeti-watcher-command
658 (($ <ganeti-watcher-configuration> ganeti _ rapi-ip job-age verify-disks?
661 (system* #$(file-append ganeti "/sbin/ganeti-watcher")
663 #~((string-append "--rapi-ip=" #$rapi-ip))
665 #$(string-append "--job-age=" (number->string job-age))
668 #~("--no-verify-disks"))
673 (define (ganeti-watcher-jobs config)
675 (($ <ganeti-watcher-configuration> _ schedule)
677 #~(job #$@(match schedule
682 #$(ganeti-watcher-command config))))))
684 (define ganeti-watcher-service-type
685 (service-type (name 'ganeti-watcher)
687 (list (service-extension mcron-service-type
688 ganeti-watcher-jobs)))
689 (default-value (ganeti-watcher-configuration))
691 "@command{ganeti-watcher} is a periodically run script that
692 performs a number of maintenance actions on the cluster. It will automatically
693 restart instances that are marked as ERROR_down, i.e., instances that should be
694 running, but are not; and it will also try to repair DRBD links in case a
695 secondary node has rebooted. In addition it is responsible for archiving old
696 cluster jobs, and it will restart any down Ganeti daemons that are appropriate
697 for the current node. If the cluster parameter @code{maintain_node_health} is
698 enabled, the watcher will also shutdown instances and DRBD devices if the node
699 is declared offline by known master candidates.")))
701 (define-record-type* <ganeti-cleaner-configuration>
702 ganeti-cleaner-configuration make-ganeti-cleaner-configuration
703 ganeti-cleaner-configuration?
704 (ganeti ganeti-cleaner-configuration-ganeti ;<package>
706 (master-schedule ganeti-cleaner-configuration-master-schedule ;list | string
707 ;; Run the master cleaner at 01:45 every day.
708 (default "45 1 * * *"))
709 (node-schedule ganeti-cleaner-configuration-node-schedule ;list | string
710 ;; Run the node cleaner at 02:45 every day.
711 (default "45 2 * * *")))
713 (define ganeti-cleaner-jobs
715 (($ <ganeti-cleaner-configuration> ganeti master-schedule node-schedule)
717 #~(job #$@(match master-schedule
719 #~(#$master-schedule))
721 #~('#$master-schedule)))
723 (system* #$(file-append ganeti "/sbin/ganeti-cleaner")
725 #~(job #$@(match node-schedule
729 #~('#$node-schedule)))
731 (system* #$(file-append ganeti "/sbin/ganeti-cleaner")
734 (define ganeti-cleaner-service-type
735 (service-type (name 'ganeti-cleaner)
737 (list (service-extension mcron-service-type
738 ganeti-cleaner-jobs)))
739 (default-value (ganeti-cleaner-configuration))
741 "@command{ganeti-cleaner} is a script that removes old files
742 from the cluster. When called with @code{node} as argument it removes expired
743 X509 certificates and keys from @file{/var/run/ganeti/crypto}, as well as
744 outdated @command{ganeti-watcher} information.
746 When called with @code{master} as argument, it instead removes files older
747 than 21 days from @file{/var/lib/ganeti/queue/archive}.")))
749 (define-record-type* <ganeti-configuration>
750 ganeti-configuration make-ganeti-configuration
751 ganeti-configuration?
752 (ganeti ganeti-configuration-ganeti
754 (noded-configuration ganeti-configuration-noded-configuration
755 (default (ganeti-noded-configuration)))
756 (confd-configuration ganeti-configuration-confd-configuration
757 (default (ganeti-confd-configuration)))
758 (wconfd-configuration ganeti-configuration-wconfd-configuration
759 (default (ganeti-wconfd-configuration)))
760 (luxid-configuration ganeti-configuration-luxid-configuration
761 (default (ganeti-luxid-configuration)))
762 (rapi-configuration ganeti-configuration-rapi-configuration
763 (default (ganeti-rapi-configuration)))
764 (kvmd-configuration ganeti-configuration-kvmd-configuration
765 (default (ganeti-kvmd-configuration)))
766 (mond-configuration ganeti-configuration-mond-configuration
767 (default (ganeti-mond-configuration)))
768 (metad-configuration ganeti-configuration-metad-configuration
769 (default (ganeti-metad-configuration)))
770 (watcher-configuration ganeti-configuration-watcher-configuration
771 (default (ganeti-watcher-configuration)))
772 (cleaner-configuration ganeti-configuration-cleaner-configuration
773 (default (ganeti-cleaner-configuration)))
774 (file-storage-paths ganeti-configuration-file-storage-paths ;list of strings | gexp
776 (os ganeti-configuration-os ;list of <ganeti-os>
779 (define (ganeti-activation config)
780 (with-imported-modules '((guix build utils))
782 (use-modules (guix build utils))
785 "/var/log/ganeti/kvm"
787 "/var/lib/ganeti/rapi"
788 "/var/lib/ganeti/queue"
789 "/var/lib/ganeti/queue/archive"
790 "/var/run/ganeti/bdev-cache"
791 "/var/run/ganeti/crypto"
792 "/var/run/ganeti/socket"
793 "/var/run/ganeti/instance-disks"
794 "/var/run/ganeti/instance-reason"
795 "/var/run/ganeti/livelocks")))))
797 (define ganeti-shepherd-services
799 (($ <ganeti-configuration> _ noded confd wconfd luxid rapi kvmd mond metad)
800 (append (ganeti-noded-service noded)
801 (ganeti-confd-service confd)
802 (ganeti-wconfd-service wconfd)
803 (ganeti-luxid-service luxid)
804 (ganeti-rapi-service rapi)
805 (ganeti-kvmd-service kvmd)
806 (ganeti-mond-service mond)
807 (ganeti-metad-service metad)))))
809 (define ganeti-mcron-jobs
811 (($ <ganeti-configuration> _ _ _ _ _ _ _ _ _ watcher cleaner)
812 (append (ganeti-watcher-jobs watcher)
813 (ganeti-cleaner-jobs cleaner)))))
815 (define-record-type* <ganeti-os>
816 ganeti-os make-ganeti-os ganeti-os?
817 (name ganeti-os-name) ;string
818 (extension ganeti-os-extension) ;string
819 (variants ganeti-os-variants ;list of <ganeti-os-variant>
822 (define-record-type* <ganeti-os-variant>
823 ganeti-os-variant make-ganeti-os-variant ganeti-os-variant?
824 (name ganeti-os-variant-name) ;string
825 (configuration ganeti-os-variant-configuration)) ;<file-like>
827 (define %debootstrap-interfaces-hook
828 (file-append ganeti-instance-debootstrap
829 "/share/doc/ganeti-instance-debootstrap/examples/interfaces"))
831 ;; The GRUB hook shipped with instance-debootstrap does not work with GRUB2.
832 ;; For convenience, provide one that work with modern Debians here.
833 ;; Note: it would be neat to reuse Guix' bootloader infrastructure instead.
834 (define %debootstrap-grub-hook
839 if [ ${#CLEANUP[*]} -gt 0 ]; then
840 LAST_ELEMENT=$((${#CLEANUP[*]}-1))
841 REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
842 for i in $REVERSE_INDEXES; do
850 mount -t proc proc $TARGET/proc
851 CLEANUP+=(\"umount $TARGET/proc\")
852 mount -t sysfs sysfs $TARGET/sys
853 CLEANUP+=(\"umount $TARGET/sys\")
854 mount -o bind /dev $TARGET/dev
855 CLEANUP+=(\"umount $TARGET/dev\")
858 GRUB_TIMEOUT_STYLE=menu
859 GRUB_CMDLINE_LINUX_DEFAULT=\"console=ttyS0,115200 net.ifnames=0\"
860 GRUB_TERMINAL=\"serial\"
861 GRUB_SERIAL_COMMAND=\"serial --unit=0 --speed=115200\"
862 ' >> $TARGET/etc/default/grub
864 # This PATH is propagated into the chroot and necessary to make grub-install
865 # and related commands visible.
866 export PATH=\"/usr/sbin:/usr/bin:/sbin:/bin:$PATH\"
868 chroot \"$TARGET\" grub-install $BLOCKDEV
869 chroot \"$TARGET\" update-grub
875 (define %default-debootstrap-hooks
876 `((10-interfaces . ,%debootstrap-interfaces-hook)
877 (90-grub . ,%debootstrap-grub-hook)))
879 (define %default-debootstrap-extra-pkgs
880 ;; Packages suitable for a fully virtualized KVM guest.
881 '("acpi-support-base" "udev" "linux-image-amd64" "openssh-server"
882 "locales-all" "grub-pc"))
884 (define-record-type* <debootstrap-configuration>
885 debootstrap-configuration make-debootstrap-configuration
886 debootstrap-configuration?
887 (hooks debootstrap-configuration-hooks ;#f | gexp | '((name . gexp))
888 (default %default-debootstrap-hooks))
889 (proxy debootstrap-configuration-proxy (default #f)) ;#f | string
890 (mirror debootstrap-configuration-mirror ;#f | string
892 (arch debootstrap-configuration-arch (default #f)) ;#f | string
893 (suite debootstrap-configuration-suite ;#f | string
895 (extra-pkgs debootstrap-configuration-extra-pkgs ;list of strings
896 (default %default-debootstrap-extra-pkgs))
897 (components debootstrap-configuration-components ;list of strings
899 (generate-cache? debootstrap-configuration-generate-cache? ;Boolean
901 (clean-cache debootstrap-configuration-clean-cache ;#f | integer
903 (partition-style debootstrap-configuration-partition-style ;#f | symbol | string
905 (partition-alignment debootstrap-configuration-partition-alignment ;#f | integer
908 (define (hooks->directory hooks)
913 (let ((names (map car hooks))
914 (files (map cdr hooks)))
915 (with-imported-modules '((guix build utils))
916 (computed-file "hooks-union"
918 (use-modules (guix build utils)
921 (with-directory-excursion #$output
922 (for-each (match-lambda
924 (let ((file-name (string-append
926 (symbol->string name))))
927 ;; Copy to the destination to ensure
928 ;; the file is executable.
929 (copy-file hook file-name)
930 (chmod file-name #o555))))
931 '#$(zip names files))))))))
934 (define-gexp-compiler (debootstrap-configuration-compiler
935 (file <debootstrap-configuration>) system target)
937 (($ <debootstrap-configuration> hooks proxy mirror arch suite extra-pkgs
938 components generate-cache? clean-cache
939 partition-style partition-alignment)
940 (let ((customize-dir (hooks->directory hooks)))
942 "debootstrap-variant"
943 #~(call-with-output-file (ungexp output "out")
949 `("PROXY=" ,proxy "\n")
952 `("MIRROR=" ,mirror "\n")
955 `("ARCH=" ,arch "\n")
958 `("SUITE=" ,suite "\n")
960 ,@(if (not (null? extra-pkgs))
961 `("EXTRA_PKGS=" ,(string-join extra-pkgs ",") "\n")
963 ,@(if (not (null? components))
964 `("COMPONENTS=" ,(string-join components ",") "\n")
967 `("CUSTOMIZE_DIR=" ,customize-dir "\n")
969 ,@(if generate-cache?
970 '("GENERATE_CACHE=yes\n")
971 '("GENERATE_CACHE=no\n"))
973 `("CLEAN_CACHE=" ,(number->string clean-cache) "\n")
975 ,@(if partition-style
976 (if (symbol? partition-style)
978 ,(symbol->string partition-style) "\n")
979 `("PARTITION_STYLE=" ,partition-style "\n"))
981 ,@(if partition-alignment
982 `("PARTITION_ALIGNMENT="
983 ,(number->string partition-alignment) "\n")
986 #:local-build? #t)))))
988 (define (ganeti-os->directory os)
989 "Return the derivation to build the configuration directory to be installed
990 in /etc/ganeti/instance-$os for OS."
991 (let* ((name (ganeti-os-name os))
992 (extension (ganeti-os-extension os))
993 (variants (ganeti-os-variants os))
994 (names (map ganeti-os-variant-name variants))
995 (configs (map ganeti-os-variant-configuration variants)))
996 (with-imported-modules '((guix build utils))
999 (use-modules (guix build utils)
1004 (unless (null? '#$names)
1005 (let ((variants-dir (string-append #$output "/variants")))
1006 (mkdir-p variants-dir)
1007 (call-with-output-file (string-append variants-dir "/variants.list")
1010 (string-join '#$names "\n"))))
1011 (for-each (match-lambda
1014 (string-append variants-dir "/" name
1017 '#$(zip names configs))))))
1019 (computed-file (string-append name "-os") builder))))
1021 (define (ganeti-directory file-storage-file os)
1022 (let ((dirs (map ganeti-os->directory os))
1023 (names (map ganeti-os-name os)))
1026 (use-modules (ice-9 match))
1028 (when #$file-storage-file
1029 (symlink #$file-storage-file
1030 (string-append #$output "/file-storage-paths")))
1031 (for-each (match-lambda
1034 (string-append #$output "/instance-" name))))
1035 '#$(zip names dirs))))
1036 (computed-file "etc-ganeti" builder)))
1038 (define (file-storage-file paths)
1041 ((? list?) (plain-file
1042 "file-storage-paths"
1043 (string-join paths "\n")))
1046 (define (ganeti-etc-service config)
1047 (list `("ganeti" ,(ganeti-directory
1049 (ganeti-configuration-file-storage-paths config))
1050 (ganeti-configuration-os config)))))
1052 (define (debootstrap-os variants)
1054 (name "debootstrap")
1056 (variants variants)))
1058 (define (debootstrap-variant name configuration)
1061 (configuration configuration)))
1063 (define %default-debootstrap-variants
1064 (list (debootstrap-variant
1066 (debootstrap-configuration))))
1068 (define (guix-os variants)
1072 (variants variants)))
1074 (define (guix-variant name configuration)
1077 (configuration configuration)))
1079 (define %default-guix-variants
1082 (file-append ganeti-instance-guix
1083 "/share/doc/ganeti-instance-guix/examples/dynamic.scm"))))
1085 ;; The OS configurations usually come with a default OS. To make them work
1086 ;; out of the box, follow suit.
1087 (define %default-ganeti-os
1088 (list (debootstrap-os %default-debootstrap-variants)
1089 (guix-os %default-guix-variants)))
1091 (define ganeti-service-type
1092 (service-type (name 'ganeti)
1094 (list (service-extension activation-service-type
1096 (service-extension shepherd-root-service-type
1097 ganeti-shepherd-services)
1098 (service-extension etc-service-type
1100 (service-extension profile-service-type
1101 (compose list ganeti-configuration-ganeti))
1102 (service-extension mcron-service-type
1103 ganeti-mcron-jobs)))
1104 (default-value (ganeti-configuration (os %default-ganeti-os)))
1106 "Ganeti is a family of services that are designed to run
1107 on a fleet of machines and facilitate deployment and maintenance of virtual
1108 servers (@dfn{instances}). It can migrate instances between nodes, automatically
1109 restart failed instances, evacuate nodes, and much more.")))