[Vmdebootstrap-devel] [PATCH 4/4] btrfs root support

Jan Gerber j at mailb.org
Thu Apr 9 10:00:15 UTC 2015


If roottype is set to btrfs, create a root subvolume to allow
snapshots and other btrfs features that depend on subvolumes.

btrfs root with grub:

    vmdebootstrap --image btrfs.img --distribution=jessie \
        --rootyype btrfs --grub

btrfs root with ext2 boot partition and grub:

    vmdebootstrap --image btrfs.img --distribution=jessie \
        --roottype btrfs --bootsize 256M --grub

btrfs root with ext2 boot partition and extlinux:

    vmdebootstrap --image btrfs.img --distribution=jessie \
        --roottype btrfs --bootsize 256M --extlinux
---
 vmdebootstrap | 49 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/vmdebootstrap b/vmdebootstrap
index 3bb9c9f..b4e9569 100755
--- a/vmdebootstrap
+++ b/vmdebootstrap
@@ -201,7 +201,16 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
                     self.message("Creating swap space")
                     self.runcmd(['mkswap', swapdev])
                 self.mkfs(rootdev, fstype=roottype)
-                rootdir = self.mount(rootdev)
+                bootdir = rootdir = self.mount(rootdev)
+                if roottype == 'btrfs':
+                    # Btrfs root requires a separate boot partition with extlinux,
+                    # default to grub if bootdev is not defined.
+                    if not bootdev and not self.settings['grub']:
+                        self.settings['grub'] = True
+                    subvol = '@'
+                    self.message("Creating subvolume for root file system: %s" % subvol)
+                    self.runcmd(['btrfs', 'subvolume', 'create', '%s/%s' % (rootdir, subvol)])
+                    rootdir = self.mount(rootdev, options=['-o', 'subvol=%s' % subvol])
                 if bootdev:
                     if self.settings['boottype']:
                         boottype = self.settings['boottype']
@@ -229,9 +238,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, bootdir)
                 elif self.settings['extlinux']:
-                    self.install_extlinux(rootdev, rootdir)
+                    self.install_extlinux(rootdev, rootdir, bootdir)
                 self.append_serial_console(rootdir)
                 self.optimize_image(rootdir)
                 if self.settings['squash']:
@@ -284,13 +293,13 @@ 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, options=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])
+        self.runcmd(['mount'] + (options or []) + [device, mount_point])
         self.mount_points.append(mount_point)
         logging.debug('mounted %s on %s', device, mount_point)
         return mount_point
@@ -422,6 +431,9 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
         if self.settings['grub']:
             include.append('grub2')
 
+        if self.settings['roottype'] == 'btrfs':
+            include.append('btrfs-tools')
+
         if not self.settings['no-kernel']:
             if self.settings['arch'] == 'i386':
                 kernel_arch = '486'
@@ -501,7 +513,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 roottype == 'btrfs':
+                f.write('%s / %s subvol=@ 0 1\n' % (rootdevstr, roottype))
+                f.write('%s /media/btrfs %s noauto,subvolid=0 0 0\n' % (rootdevstr, roottype))
+                os.mkdir(os.path.join(rootdir, 'media', 'btrfs'))
+            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:
@@ -599,7 +616,7 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
             with open(inittab, 'a') as f:
                 f.write('\nS0:23:respawn:%s\n' % serial_command)
 
-    def install_grub2(self, rootdev, rootdir):
+    def install_grub2(self, rootdev, rootdir, bootdir):
         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])
@@ -625,24 +642,26 @@ 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, bootdir)
         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, bootdir):
         if not os.path.exists("/usr/bin/extlinux"):
             self.message("extlinux not installed, skipping.")
             return
         self.message('Installing extlinux')
 
         def find(pattern):
-            dirname = os.path.join(rootdir, 'boot')
+            dirname = os.path.join(bootdir, 'boot')
+            if not os.path.exists(dirname):
+                dirname = bootdir
             basenames = os.listdir(dirname)
             logging.debug('find: %s', basenames)
             for basename in basenames:
                 if re.search(pattern, basename):
-                    return os.path.join('boot', basename)
+                    return basename if dirname == bootdir else os.path.join('boot', basename)
             raise cliapp.AppException('Cannot find match: %s' % pattern)
 
         try:
@@ -657,23 +676,25 @@ 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(bootdir, '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 ''
+        rootflags = 'rootflags=subvol=@' if self.settings['roottype'] == 'btrfs' else ''
         msg = '''
 default linux
 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': rootflags,  # pylint: disable=bad-continuation
             'extserial': extserial,  # pylint: disable=bad-continuation
         }  # pylint: disable=bad-continuation
         logging.debug("extlinux config:\n%s", msg)
@@ -684,7 +705,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', bootdir])
         self.runcmd(['sync'])
         time.sleep(2)
 
-- 
2.1.4




More information about the Vmdebootstrap-devel mailing list