gnu: Add ghc-aeson-pretty.
[jackhill/guix/guix.git] / gnu / system / vm.scm
CommitLineData
04086015 1;;; GNU Guix --- Functional package management for GNU
29824d80 2;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
944d2b17
CAW
3;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
4;;; Copyright © 2016 Leo Famulari <leo@famulari.name>
04086015
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (gnu system vm)
93d44bd8 22 #:use-module (guix config)
04086015 23 #:use-module (guix store)
02100028 24 #:use-module (guix gexp)
04086015
LC
25 #:use-module (guix derivations)
26 #:use-module (guix packages)
d9f0a237 27 #:use-module (guix monads)
fcf63cf8
LC
28 #:use-module (guix records)
29
548f7a8f 30 #:use-module ((gnu build vm)
66670cf3 31 #:select (qemu-command))
bdb36958 32 #:use-module (gnu packages base)
1b89a66e 33 #:use-module (gnu packages guile)
bdb36958 34 #:use-module (gnu packages gawk)
1b89a66e 35 #:use-module (gnu packages bash)
4f62d8d6 36 #:use-module (gnu packages less)
04086015 37 #:use-module (gnu packages qemu)
cc4a2aeb 38 #:use-module (gnu packages disk)
5b16ff09 39 #:use-module (gnu packages zile)
04086015
LC
40 #:use-module (gnu packages grub)
41 #:use-module (gnu packages linux)
30f25b03 42 #:use-module (gnu packages package-management)
04086015
LC
43 #:use-module ((gnu packages make-bootstrap)
44 #:select (%guile-static-stripped))
9de46ffb 45 #:use-module (gnu packages admin)
0ded70f3
LC
46
47 #:use-module (gnu system shadow)
6e828634 48 #:use-module (gnu system pam)
735c6dd7 49 #:use-module (gnu system linux-initrd)
0ded70f3 50 #:use-module (gnu system grub)
c5df1839 51 #:use-module (gnu system file-systems)
033adfe7 52 #:use-module (gnu system)
db4fdc04 53 #:use-module (gnu services)
0ded70f3 54
ca85d7bc 55 #:use-module (srfi srfi-1)
04086015
LC
56 #:use-module (srfi srfi-26)
57 #:use-module (ice-9 match)
0ded70f3 58
04086015 59 #:export (expression->derivation-in-linux-vm
aedb72fb 60 qemu-image
e9f693d0 61 virtualized-operating-system
fd3bfc44 62 system-qemu-image
fcf63cf8 63
fd3bfc44 64 system-qemu-image/shared-store
1e77fedb
LC
65 system-qemu-image/shared-store-script
66 system-disk-image))
04086015
LC
67
68\f
69;;; Commentary:
70;;;
71;;; Tools to evaluate build expressions within virtual machines.
72;;;
73;;; Code:
74
83bcd0b8
LC
75(define %linux-vm-file-systems
76 ;; File systems mounted for 'derivation-in-linux-vm'. The store and /xchg
77 ;; directory are shared with the host over 9p.
78 (list (file-system
79 (mount-point (%store-prefix))
80 (device "store")
81 (type "9p")
82 (needed-for-boot? #t)
3c05b4bc
LC
83 (options "trans=virtio")
84 (check? #f))
83bcd0b8
LC
85 (file-system
86 (mount-point "/xchg")
87 (device "xchg")
88 (type "9p")
89 (needed-for-boot? #t)
3c05b4bc
LC
90 (options "trans=virtio")
91 (check? #f))))
83bcd0b8 92
d9f0a237 93(define* (expression->derivation-in-linux-vm name exp
04086015 94 #:key
2455085a 95 (system (%current-system))
04086015 96 (linux linux-libre)
735c6dd7 97 initrd
06da1a6b 98 (qemu qemu-minimal)
04086015 99 (env-vars '())
1aa0033b 100 (modules
548f7a8f
LC
101 '((gnu build vm)
102 (gnu build install)
8a9e21d1 103 (gnu build linux-boot)
0e704a2d 104 (gnu build linux-modules)
e2f4b305 105 (gnu build file-systems)
0e704a2d 106 (guix elf)
72b891e5 107 (guix records)
6fd1a796 108 (guix build utils)
1e49bcf9 109 (guix build syscalls)
6eb43907 110 (guix build bournish)
6fd1a796 111 (guix build store-copy)))
04086015
LC
112 (guile-for-build
113 (%guile-for-build))
114
115 (make-disk-image? #f)
ca85d7bc 116 (references-graphs #f)
defa1b9b 117 (memory-size 256)
c4a74364 118 (disk-image-format "qcow2")
04086015
LC
119 (disk-image-size
120 (* 100 (expt 2 20))))
735c6dd7 121 "Evaluate EXP in a QEMU virtual machine running LINUX with INITRD (a
1aa0033b 122derivation). In the virtual machine, EXP has access to all its inputs from the
735c6dd7 123store; it should put its output files in the `/xchg' directory, which is
defa1b9b
LC
124copied to the derivation's output when the VM terminates. The virtual machine
125runs with MEMORY-SIZE MiB of memory.
04086015 126
c4a74364
LC
127When MAKE-DISK-IMAGE? is true, then create a QEMU disk image of type
128DISK-IMAGE-FORMAT (e.g., 'qcow2' or 'raw'), of DISK-IMAGE-SIZE bytes and
129return it.
ca85d7bc 130
1aa0033b 131MODULES is the set of modules imported in the execution environment of EXP.
ade5ce7a 132
ca85d7bc
LC
133When REFERENCES-GRAPHS is true, it must be a list of file name/store path
134pairs, as for `derivation'. The files containing the reference graphs are
135made available under the /xchg CIFS share."
d9f0a237 136 (mlet* %store-monad
1aa0033b
LC
137 ((module-dir (imported-modules modules))
138 (compiled (compiled-modules modules))
139 (user-builder (gexp->file "builder-in-linux-vm" exp))
02100028
LC
140 (loader (gexp->file "linux-vm-loader"
141 #~(begin
142 (set! %load-path
143 (cons #$module-dir %load-path))
144 (set! %load-compiled-path
145 (cons #$compiled
146 %load-compiled-path))
147 (primitive-load #$user-builder))))
bdb36958 148 (coreutils -> (canonical-package coreutils))
d4254711 149 (initrd (if initrd ; use the default initrd?
735c6dd7 150 (return initrd)
060238ae 151 (base-initrd %linux-vm-file-systems
0d275f4a 152 #:linux linux
24e0160a 153 #:virtio? #t
6c1df081 154 #:qemu-networking? #t))))
1aa0033b
LC
155
156 (define builder
157 ;; Code that launches the VM that evaluates EXP.
158 #~(begin
159 (use-modules (guix build utils)
548f7a8f 160 (gnu build vm))
1aa0033b
LC
161
162 (let ((inputs '#$(list qemu coreutils))
163 (linux (string-append #$linux "/bzImage"))
164 (initrd (string-append #$initrd "/initrd"))
165 (loader #$loader)
166 (graphs '#$(match references-graphs
167 (((graph-files . _) ...) graph-files)
168 (_ #f))))
169
170 (set-path-environment-variable "PATH" '("bin") inputs)
171
172 (load-in-linux-vm loader
173 #:output #$output
174 #:linux linux #:initrd initrd
175 #:memory-size #$memory-size
176 #:make-disk-image? #$make-disk-image?
c4a74364 177 #:disk-image-format #$disk-image-format
1aa0033b
LC
178 #:disk-image-size #$disk-image-size
179 #:references-graphs graphs))))
180
181 (gexp->derivation name builder
182 ;; TODO: Require the "kvm" feature.
183 #:system system
184 #:env-vars env-vars
5ce3defe 185 #:modules modules
1aa0033b
LC
186 #:guile-for-build guile-for-build
187 #:references-graphs references-graphs)))
d9f0a237
LC
188
189(define* (qemu-image #:key
04086015
LC
190 (name "qemu-image")
191 (system (%current-system))
06da1a6b 192 (qemu qemu-minimal)
04086015 193 (disk-image-size (* 100 (expt 2 20)))
c4a74364 194 (disk-image-format "qcow2")
03ddfaf5 195 (file-system-type "ext4")
ef9fc40d 196 file-system-label
f2c403ea 197 os-derivation
0e2ddecd 198 grub-configuration
150e20dd 199 (register-closures? #t)
150e20dd
LC
200 (inputs '())
201 copy-inputs?)
c4a74364 202 "Return a bootable, stand-alone QEMU image of type DISK-IMAGE-FORMAT (e.g.,
ef9fc40d
LC
203'qcow2' or 'raw'), with a root partition of type FILE-SYSTEM-TYPE.
204Optionally, FILE-SYSTEM-LABEL can be specified as the volume name for the root
f2c403ea
LC
205partition. The returned image is a full disk image that runs OS-DERIVATION,
206with a GRUB installation that uses GRUB-CONFIGURATION as its configuration
207file (GRUB-CONFIGURATION must be the name of a file in the VM.)
93d44bd8 208
150e20dd
LC
209INPUTS is a list of inputs (as for packages). When COPY-INPUTS? is true, copy
210all of INPUTS into the image being built. When REGISTER-CLOSURES? is true,
211register INPUTS in the store database of the image so that Guix can be used in
b4140694 212the image."
b53833b2
LC
213 (expression->derivation-in-linux-vm
214 name
215 #~(begin
216 (use-modules (gnu build vm)
217 (guix build utils))
1aa0033b 218
b53833b2 219 (let ((inputs
ec2406ef 220 '#$(append (list qemu parted grub e2fsprogs)
b53833b2
LC
221 (map canonical-package
222 (list sed grep coreutils findutils gawk))
223 (if register-closures? (list guix) '())))
1aa0033b 224
b53833b2
LC
225 ;; This variable is unused but allows us to add INPUTS-TO-COPY
226 ;; as inputs.
227 (to-register
228 '#$(map (match-lambda
229 ((name thing) thing)
230 ((name thing output) `(,thing ,output)))
231 inputs)))
1aa0033b 232
b53833b2 233 (set-path-environment-variable "PATH" '("bin" "sbin") inputs)
1aa0033b 234
72b891e5
LC
235 (let* ((graphs '#$(match inputs
236 (((names . _) ...)
237 names)))
238 (initialize (root-partition-initializer
239 #:closures graphs
240 #:copy-closures? #$copy-inputs?
241 #:register-closures? #$register-closures?
242 #:system-directory #$os-derivation))
243 (partitions (list (partition
244 (size #$(- disk-image-size
245 (* 10 (expt 2 20))))
246 (label #$file-system-label)
247 (file-system #$file-system-type)
248 (bootable? #t)
249 (initializer initialize)))))
b53833b2 250 (initialize-hard-disk "/dev/vda"
72b891e5
LC
251 #:partitions partitions
252 #:grub.cfg #$grub-configuration)
b53833b2
LC
253 (reboot))))
254 #:system system
255 #:make-disk-image? #t
256 #:disk-image-size disk-image-size
257 #:disk-image-format disk-image-format
258 #:references-graphs inputs))
04086015
LC
259
260\f
261;;;
1e77fedb 262;;; VM and disk images.
04086015
LC
263;;;
264
1e77fedb
LC
265(define* (system-disk-image os
266 #:key
56ef7fcc 267 (name "disk-image")
1e77fedb
LC
268 (file-system-type "ext4")
269 (disk-image-size (* 900 (expt 2 20)))
270 (volatile? #t))
271 "Return the derivation of a disk image of DISK-IMAGE-SIZE bytes of the
272system described by OS. Said image can be copied on a USB stick as is. When
273VOLATILE? is true, the root file system is made volatile; this is useful
274to USB sticks meant to be read-only."
10ace2c4
LC
275 (define root-label
276 ;; Volume name of the root file system. Since we don't know which device
277 ;; will hold it, we use the volume name to find it (using the UUID would
278 ;; be even better, but somewhat less convenient.)
279 "gnu-disk-image")
280
1e77fedb
LC
281 (define file-systems-to-keep
282 (remove (lambda (fs)
283 (string=? (file-system-mount-point fs) "/"))
284 (operating-system-file-systems os)))
285
286 (let ((os (operating-system (inherit os)
932e1f92 287 ;; Since this is meant to be used on real hardware, don't
493c245b
LC
288 ;; install QEMU networking or anything like that. Assume USB
289 ;; mass storage devices (usb-storage.ko) are available.
52ac153e
LC
290 (initrd (lambda (file-systems . rest)
291 (apply base-initrd file-systems
292 #:volatile-root? #t
52ac153e 293 rest)))
1e77fedb
LC
294
295 ;; Force our own root file system.
296 (file-systems (cons (file-system
297 (mount-point "/")
10ace2c4 298 (device root-label)
d4c87617 299 (title 'label)
1e77fedb
LC
300 (type file-system-type))
301 file-systems-to-keep)))))
302
303 (mlet* %store-monad ((os-drv (operating-system-derivation os))
304 (grub.cfg (operating-system-grub.cfg os)))
56ef7fcc 305 (qemu-image #:name name
f2c403ea 306 #:os-derivation os-drv
56ef7fcc 307 #:grub-configuration grub.cfg
1e77fedb
LC
308 #:disk-image-size disk-image-size
309 #:disk-image-format "raw"
310 #:file-system-type file-system-type
10ace2c4 311 #:file-system-label root-label
1e77fedb
LC
312 #:copy-inputs? #t
313 #:register-closures? #t
314 #:inputs `(("system" ,os-drv)
315 ("grub.cfg" ,grub.cfg))))))
316
0b14d1d7 317(define* (system-qemu-image os
66f23d66
LC
318 #:key
319 (file-system-type "ext4")
320 (disk-image-size (* 900 (expt 2 20))))
321 "Return the derivation of a freestanding QEMU image of DISK-IMAGE-SIZE bytes
322of the GNU system as described by OS."
1eeccc2f
LC
323 (define file-systems-to-keep
324 ;; Keep only file systems other than root and not normally bound to real
325 ;; devices.
326 (remove (lambda (fs)
327 (let ((target (file-system-mount-point fs))
328 (source (file-system-device fs)))
329 (or (string=? target "/")
330 (string-prefix? "/dev/" source))))
331 (operating-system-file-systems os)))
332
66f23d66 333 (let ((os (operating-system (inherit os)
e84d8b30 334 ;; Use an initrd with the whole QEMU shebang.
52ac153e
LC
335 (initrd (lambda (file-systems . rest)
336 (apply base-initrd file-systems
337 #:virtio? #t
338 #:qemu-networking? #t
339 rest)))
e84d8b30 340
1eeccc2f
LC
341 ;; Force our own root file system.
342 (file-systems (cons (file-system
66f23d66
LC
343 (mount-point "/")
344 (device "/dev/sda1")
1eeccc2f
LC
345 (type file-system-type))
346 file-systems-to-keep)))))
66f23d66
LC
347 (mlet* %store-monad
348 ((os-drv (operating-system-derivation os))
b4140694 349 (grub.cfg (operating-system-grub.cfg os)))
f2c403ea
LC
350 (qemu-image #:os-derivation os-drv
351 #:grub-configuration grub.cfg
66f23d66
LC
352 #:disk-image-size disk-image-size
353 #:file-system-type file-system-type
b4140694
LC
354 #:inputs `(("system" ,os-drv)
355 ("grub.cfg" ,grub.cfg))
150e20dd 356 #:copy-inputs? #t))))
04086015 357
fcf63cf8
LC
358\f
359;;;
360;;; VMs that share file systems with the host.
361;;;
362
96ffa27b
LC
363(define (file-system->mount-tag fs)
364 "Return a 9p mount tag for host file system FS."
365 ;; QEMU mount tags cannot contain slashes and cannot start with '_'.
366 ;; Compute an identifier that corresponds to the rules.
367 (string-append "TAG"
368 (string-map (match-lambda
369 (#\/ #\_)
370 (chr chr))
371 fs)))
372
fcf63cf8
LC
373(define (mapping->file-system mapping)
374 "Return a 9p file system that realizes MAPPING."
375 (match mapping
376 (($ <file-system-mapping> source target writable?)
377 (file-system
378 (mount-point target)
379 (device (file-system->mount-tag source))
380 (type "9p")
381 (flags (if writable? '() '(read-only)))
382 (options (string-append "trans=virtio"))
383 (check? #f)
384 (create-mount-point? #t)))))
385
386(define (virtualized-operating-system os mappings)
83bcd0b8 387 "Return an operating system based on OS suitable for use in a virtualized
fcf63cf8
LC
388environment with the store shared with the host. MAPPINGS is a list of
389<file-system-mapping> to realize in the virtualized OS."
390 (define user-file-systems
391 ;; Remove file systems that conflict with those added below, or that are
392 ;; normally bound to real devices.
393 (remove (lambda (fs)
394 (let ((target (file-system-mount-point fs))
395 (source (file-system-device fs)))
396 (or (string=? target (%store-prefix))
397 (string=? target "/")
29824d80
LC
398 (and (eq? 'device (file-system-title fs))
399 (string-prefix? "/dev/" source)))))
fcf63cf8
LC
400 (operating-system-file-systems os)))
401
83bcd0b8 402 (operating-system (inherit os)
52ac153e
LC
403 (initrd (lambda (file-systems . rest)
404 (apply base-initrd file-systems
405 #:volatile-root? #t
406 #:virtio? #t
407 #:qemu-networking? #t
408 rest)))
65fb4515
LC
409
410 ;; Disable swap.
411 (swap-devices '())
412
1eeccc2f
LC
413 (file-systems (cons* (file-system
414 (mount-point "/")
415 (device "/dev/vda1")
416 (type "ext4"))
96ffa27b
LC
417
418 (file-system (inherit
fcf63cf8 419 (mapping->file-system %store-mapping))
96ffa27b 420 (needed-for-boot? #t))
1eeccc2f 421
fcf63cf8
LC
422 (append (map mapping->file-system mappings)
423 user-file-systems)))))
83bcd0b8 424
fd3bfc44 425(define* (system-qemu-image/shared-store
0b14d1d7 426 os
6aa260af
LC
427 #:key
428 full-boot?
4c0416ae 429 (disk-image-size (* (if full-boot? 500 30) (expt 2 20))))
fd3bfc44 430 "Return a derivation that builds a QEMU image of OS that shares its store
6aa260af
LC
431with the host.
432
433When FULL-BOOT? is true, return an image that does a complete boot sequence,
434bootloaded included; thus, make a disk image that contains everything the
435bootloader refers to: OS kernel, initrd, bootloader data, etc."
436 (mlet* %store-monad ((os-drv (operating-system-derivation os))
437 (grub.cfg (operating-system-grub.cfg os)))
438 ;; XXX: When FULL-BOOT? is true, we end up creating an image that contains
439 ;; GRUB.CFG and all its dependencies, including the output of OS-DRV.
440 ;; This is more than needed (we only need the kernel, initrd, GRUB for its
441 ;; font, and the background image), but it's hard to filter that.
f2c403ea
LC
442 (qemu-image #:os-derivation os-drv
443 #:grub-configuration grub.cfg
150e20dd 444 #:disk-image-size disk-image-size
6aa260af
LC
445 #:inputs (if full-boot?
446 `(("grub.cfg" ,grub.cfg))
447 '())
150e20dd
LC
448
449 ;; XXX: Passing #t here is too slow, so let it off by default.
450 #:register-closures? #f
6aa260af 451 #:copy-inputs? full-boot?)))
fd3bfc44 452
96ffa27b
LC
453(define* (common-qemu-options image shared-fs)
454 "Return the a string-value gexp with the common QEMU options to boot IMAGE,
455with '-virtfs' options for the host file systems listed in SHARED-FS."
456 (define (virtfs-option fs)
457 #~(string-append "-virtfs local,path=\"" #$fs
458 "\",security_model=none,mount_tag=\""
459 #$(file-system->mount-tag fs)
460 "\" "))
461
462 #~(string-append
944d2b17
CAW
463 ;; Only enable kvm if we see /dev/kvm exists.
464 ;; This allows users without hardware virtualization to still use these
465 ;; commands.
466 #$(if (file-exists? "/dev/kvm")
467 " -enable-kvm "
468 "")
469 " -no-reboot -net nic,model=virtio \
96ffa27b 470 " #$@(map virtfs-option shared-fs) " \
3c1f0e3b 471 -net user \
957afcae 472 -vga std \
3c1f0e3b
LC
473 -drive file=" #$image
474 ",if=virtio,cache=writeback,werror=report,readonly \
810568b3 475 -m 256"))
3c1f0e3b 476
ab11f0be
LC
477(define* (system-qemu-image/shared-store-script os
478 #:key
479 (qemu qemu)
480 (graphic? #t)
fcf63cf8 481 (mappings '())
6aa260af
LC
482 full-boot?
483 (disk-image-size
4c0416ae 484 (* (if full-boot? 500 30)
6aa260af 485 (expt 2 20))))
fd3bfc44 486 "Return a derivation that builds a script to run a virtual machine image of
6aa260af
LC
487OS that shares its store with the host.
488
fcf63cf8
LC
489MAPPINGS is a list of <file-system-mapping> specifying mapping of host file
490systems into the guest.
491
6aa260af
LC
492When FULL-BOOT? is true, the returned script runs everything starting from the
493bootloader; otherwise it directly starts the operating system kernel. The
494DISK-IMAGE-SIZE parameter specifies the size in bytes of the root disk image;
495it is mostly useful when FULL-BOOT? is true."
fcf63cf8 496 (mlet* %store-monad ((os -> (virtualized-operating-system os mappings))
6aa260af
LC
497 (os-drv (operating-system-derivation os))
498 (image (system-qemu-image/shared-store
499 os
500 #:full-boot? full-boot?
501 #:disk-image-size disk-image-size)))
fd3bfc44 502 (define builder
02100028
LC
503 #~(call-with-output-file #$output
504 (lambda (port)
505 (display
506 (string-append "#!" #$bash "/bin/sh
66670cf3 507exec " #$qemu "/bin/" #$(qemu-command (%current-system))
ab11f0be
LC
508
509#$@(if full-boot?
510 #~()
511 #~(" -kernel " #$(operating-system-kernel os) "/bzImage \
512 -initrd " #$os-drv "/initrd \
513 -append \"" #$(if graphic? "" "console=ttyS0 ")
ee2a6304
LC
514 "--system=" #$os-drv " --load=" #$os-drv "/boot --root=/dev/vda1 "
515 (string-join (list #+@(operating-system-kernel-arguments os))) "\" "))
fcf63cf8
LC
516#$(common-qemu-options image
517 (map file-system-mapping-source
518 (cons %store-mapping mappings)))
810568b3 519" \"$@\"\n")
02100028
LC
520 port)
521 (chmod port #o555))))
522
523 (gexp->derivation "run-vm.sh" builder)))
fd3bfc44 524
04086015 525;;; vm.scm ends here