diff --git a/vmdebootstrap b/vmdebootstrap index 4895147..c46be43 100755 --- a/vmdebootstrap +++ b/vmdebootstrap @@ -218,6 +218,25 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth self.runcmd(['mkswap', swapdev]) self.mkfs(rootdev, fstype=roottype) rootdir = self.mount(rootdev) + rootfsdir = rootdir + if 'btrfs' == roottype: + # Put root in a subvolume, to ease snapshots and volume management + self.message("Creating root file system as btrfs subvolume @") + self.runcmd(['btrfs', 'subvolume', 'create', "%s/@" % rootdir]) + + # Make sure the subvolume mount point show in in + # /proc/mounts for grub-update to figure out the + # device for the root file system. + newrootdir = "%s/build" % rootdir + os.mkdir(newrootdir) + self.mount(rootdev, newrootdir, ['-o','subvol=@']) +# self.runcmd(['btrfs', 'subvolume', 'set-default', '@', rootdir]) + + # Make the btrfs root file system available in the chroot. + os.mkdir("%s/btrfs" % newrootdir) + self.mount(rootdev, "%s/btrfs" % newrootdir) + + rootdir = newrootdir if bootdev: if self.settings['boottype']: boottype = self.settings['boottype'] @@ -245,9 +264,9 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if self.settings['image']: if self.settings['grub']: - self.install_grub2(rootdev, rootdir) + self.install_grub2(rootdev, rootdir, rootfsdir) elif self.settings['extlinux']: - self.install_extlinux(rootdev, rootdir) + self.install_extlinux(rootdev, rootdir, rootfsdir) self.append_serial_console(rootdir) self.optimize_image(rootdir) if self.settings['squash']: @@ -300,13 +319,19 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth logging.debug('mkdir %s', dirname) return dirname - def mount(self, device, path=None): + def mount(self, device, path=None, opts=None): if not path: mount_point = self.mkdtemp() else: mount_point = path self.message('Mounting %s on %s' % (device, mount_point)) - self.runcmd(['mount', device, mount_point]) + cmd = ['mount'] + if opts is not None: + for opt in opts: + cmd.append(opt) + cmd.append(device) + cmd.append(mount_point) + self.runcmd(cmd) self.mount_points.append(mount_point) logging.debug('mounted %s on %s', device, mount_point) return mount_point @@ -458,6 +483,9 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if self.settings['grub']: include.append('grub-pc') + if 'btrfs' == self.settings['roottype']: + include.append('btrfs-tools') + if not self.settings['no-kernel']: if self.settings['kernel-package']: kernel_image = self.settings['kernel-package'] @@ -546,7 +574,12 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth fstab = os.path.join(rootdir, 'etc', 'fstab') with open(fstab, 'w') as f: f.write('proc /proc proc defaults 0 0\n') - f.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr, roottype)) + if 'btrfs' == roottype: +# f.write('%s / %s defaults 0 1\n' % (rootdevstr, roottype)) + f.write('%s / %s subvol=@ 0 1\n' % (rootdevstr, roottype)) + f.write('%s /btrfs %s defaults 1\n' % (rootdevstr, roottype)) + else: + f.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr, roottype)) if bootdevstr: f.write('%s /boot %s errors=remount-ro 0 2\n' % (bootdevstr, boottype)) if self.settings['swap'] > 0: @@ -661,7 +694,8 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth cfg.write("%s\n" % terminal) cfg.write("%s\n" % command) - def install_grub2(self, rootdev, rootdir): + def install_grub2(self, rootdev, rootdir, rootfsdir): + # FIXME use rootfsdir self.message("Configuring grub2") # rely on kpartx using consistent naming to map loop0p1 to loop0 install_dev = os.path.join('/dev', os.path.basename(rootdev)[:-2]) @@ -679,12 +713,12 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth self.runcmd(['chroot', rootdir, 'grub-install', install_dev]) except cliapp.AppException: self.message("Failed. Is grub2-common installed? Using extlinux.") - self.install_extlinux(rootdev, rootdir) + self.install_extlinux(rootdev, rootdir, rootfsdir) self.runcmd(['umount', os.path.join(rootdir, 'sys')]) self.runcmd(['umount', os.path.join(rootdir, 'proc')]) self.runcmd(['umount', os.path.join(rootdir, 'dev')]) - def install_extlinux(self, rootdev, rootdir): + def install_extlinux(self, rootdev, rootdir, rootfsdir): if not os.path.exists("/usr/bin/extlinux"): self.message("extlinux not installed, skipping.") return @@ -711,7 +745,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth '-s', 'UUID', rootdev]) uuid = out.splitlines()[0].strip() - conf = os.path.join(rootdir, 'extlinux.conf') + conf = os.path.join(rootfsdir, 'extlinux.conf') logging.debug('configure extlinux %s', conf) kserial = 'console=ttyS0,115200' if self.settings['serial-console'] else '' extserial = 'serial 0 115200' if self.settings['serial-console'] else '' @@ -721,13 +755,14 @@ timeout 1 label linux kernel %(kernel)s -append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s +append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s %(rootflags)s %(extserial)s ''' % { 'kernel': kernel_image, # pylint: disable=bad-continuation 'initrd': initrd_image, # pylint: disable=bad-continuation 'uuid': uuid, # pylint: disable=bad-continuation 'kserial': kserial, # pylint: disable=bad-continuation + 'rootflags': 'rootfsflags=subvol=@' if 'btrfs' == self.settings['roottype'] else '', # pylint: disable=bad-continuation 'extserial': extserial, # pylint: disable=bad-continuation } # pylint: disable=bad-continuation logging.debug("extlinux config:\n%s", msg) @@ -738,7 +773,7 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s f = open(conf, 'w') f.write(msg) - self.runcmd(['extlinux', '--install', rootdir]) + self.runcmd(['extlinux', '--install', rootfsdir]) self.runcmd(['sync']) time.sleep(2)