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