X-Git-Url: https://git.siccegge.de//index.cgi?a=blobdiff_plain;f=vmdebootstrap;h=49c21c89bcc28e9350b478ff41654106377f11e1;hb=c916e345d48735abd2decc88a781841edf65ac84;hp=e736c604ce108422bcc6e8dfb68c54c910516111;hpb=05dcc3af8835fce167b6ddd9f29d8c72ae0e8a58;p=forks%2Fvmdebootstrap.git diff --git a/vmdebootstrap b/vmdebootstrap index e736c60..49c21c8 100755 --- a/vmdebootstrap +++ b/vmdebootstrap @@ -27,7 +27,7 @@ import tempfile import time -__version__ = '0.6' +__version__ = '0.7' # pylint: disable=invalid-name @@ -70,6 +70,13 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth ['part-type'], 'Partition type to use for this image', default='msdos') + self.settings.string( + ['roottype'], + 'specify file system type for /', + default='ext4') + self.settings.bytesize( + ['swap'], + 'create swap space of size SIZE (min 256Mb)') self.settings.string( ['foreign'], 'set up foreign debootstrap environment using provided program (ie binfmt handler)') @@ -158,13 +165,13 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth 'Create an apt source based on the distribution and mirror selected.') self.settings.boolean( ['mbr'], - 'Run install-mbr (no longer done by default)') + 'Run install-mbr (default if extlinux used)') self.settings.boolean( ['grub'], 'Install and configure grub2 - disables extlinux.') self.settings.boolean( ['sparse'], - 'Dont fill the image with zeros to keep a sparse disk image', + 'Do not fill the image with zeros to keep a sparse disk image', default=False) self.settings.boolean( ['pkglist'], @@ -181,15 +188,18 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth rootdir = None try: rootdev = None - roottype = 'ext4' + roottype = self.settings['roottype'] bootdev = None boottype = None if self.settings['image']: self.create_empty_image() self.partition_image() - if self.settings['mbr']: + if self.settings['mbr'] or self.settings['extlinux']: self.install_mbr() - (rootdev, bootdev) = self.setup_kpartx() + (rootdev, bootdev, swapdev) = self.setup_kpartx() + if self.settings['swap'] > 0: + self.message("Creating swap space") + self.runcmd(['mkswap', swapdev]) self.mkfs(rootdev, fstype=roottype) rootdir = self.mount(rootdev) if bootdev: @@ -238,7 +248,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if self.settings['owner']: self.chown() - except BaseException, e: + except BaseException as e: self.message('EEEK! Something bad happened...') if rootdir: db_log = os.path.join(rootdir, 'debootstrap', 'debootstrap.log') @@ -303,6 +313,15 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth 'mklabel', self.settings['part-type']]) partoffset = 0 bootsize = 0 + extent = '100%' + swap = 256 * 1024 * 1024 + if self.settings['swap'] > 0: + if self.settings['swap'] > swap: + swap = self.settings['swap'] + else: + # minimum 256Mb as default qemu ram is 128Mb + logging.debug("Setting minimum 256Mb swap space") + extent = "%s%%" % int(100 * (self.settings['size'] - swap) / self.settings['size']) if self.settings['bootoffset'] and self.settings['bootoffset'] is not '0': # turn v.small offsets into something at least possible to create. if self.settings['bootoffset'] < 1048576: @@ -314,21 +333,26 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth partoffset = self.settings['bootoffset'] / (1024 * 1024) self.message("Using bootoffset: %smib %s bytes" % (partoffset, self.settings['bootoffset'])) if self.settings['bootsize'] and self.settings['bootsize'] is not '0%': + if self.settings['grub'] and not partoffset: + partoffset = 1 bootsize = self.settings['bootsize'] / (1024 * 1024) bootsize += partoffset self.message("Using bootsize %smib: %s bytes" % (bootsize, self.settings['bootsize'])) logging.debug("Starting boot partition at %sMb", bootsize) self.runcmd(['parted', '-s', self.settings['image'], 'mkpart', 'primary', 'fat16', str(partoffset), str(bootsize)]) - if partoffset == 0: + logging.debug("Starting root partition at %sMb", partoffset) self.runcmd(['parted', '-s', self.settings['image'], - 'mkpart', 'primary', '0%', '100%']) + 'mkpart', 'primary', str(bootsize), extent]) else: - logging.debug("Starting root partition at %sMb", partoffset) self.runcmd(['parted', '-s', self.settings['image'], - 'mkpart', 'primary', str(bootsize), '100%']) + 'mkpart', 'primary', '0%', extent]) self.runcmd(['parted', '-s', self.settings['image'], 'set', '1', 'boot', 'on']) + if self.settings['swap'] > 0: + logging.debug("Creating swap partition") + self.runcmd(['parted', '-s', self.settings['image'], + 'mkpart', 'primary', 'linux-swap', extent, '100%']) def update_initramfs(self, rootdir): cmd = os.path.join('usr', 'sbin', 'update-initramfs') @@ -340,27 +364,47 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if os.path.exists("/sbin/install-mbr"): self.message('Installing MBR') self.runcmd(['install-mbr', self.settings['image']]) + else: + msg = "mbr enabled but /sbin/install-mbr not found" \ + " - please install the mbr package." + raise cliapp.AppException(msg) def setup_kpartx(self): bootindex = None + swapindex = None out = self.runcmd(['kpartx', '-avs', self.settings['image']]) - if self.settings['bootsize']: + if self.settings['bootsize'] and self.settings['swap'] > 0: + bootindex = 0 + rootindex = 1 + swapindex = 2 + parts = 3 + elif self.settings['bootsize']: bootindex = 0 rootindex = 1 parts = 2 + elif self.settings['swap'] > 0: + rootindex = 0 + swapindex = 1 + parts = 2 else: rootindex = 0 parts = 1 boot = None + swap = None devices = [line.split()[2] for line in out.splitlines() if line.startswith('add map ')] if len(devices) != parts: - raise cliapp.AppException('Surprising number of partitions') + msg = 'Surprising number of partitions - check output of losetup -a' + logging.debug("%s", self.runcmd(['losetup', '-a'])) + logging.debug("%s: devices=%s parts=%s", msg, devices, parts) + raise cliapp.AppException(msg) root = '/dev/mapper/%s' % devices[rootindex] if self.settings['bootsize']: boot = '/dev/mapper/%s' % devices[bootindex] - return root, boot + if self.settings['swap'] > 0: + swap = '/dev/mapper/%s' % devices[swapindex] + return root, boot, swap def mkfs(self, device, fstype): self.message('Creating filesystem %s' % fstype) @@ -376,11 +420,13 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth include.append('acpid') if self.settings['grub']: - include.append('grub2') + include.append('grub-pc') if not self.settings['no-kernel']: if self.settings['arch'] == 'i386': kernel_arch = '486' + elif self.settings['arch'] == 'armhf': + kernel_arch = 'armmp' else: kernel_arch = self.settings['arch'] kernel_image = 'linux-image-%s' % kernel_arch @@ -460,6 +506,10 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth 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: + f.write("/dev/sda3 swap swap defaults 0 0\n") + elif self.settings['swap'] > 0: + f.write("/dev/sda2 swap swap defaults 0 0\n") def install_debs(self, rootdir): if not self.settings['custom-package']: @@ -551,6 +601,19 @@ 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) + # pylint: disable=no-self-use + def _grub_serial_console(self, rootdir): + cmdline = 'GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=tty1 console=ttyS0,38400n8"' + terminal = 'GRUB_TERMINAL="serial gfxterm"' + command = 'GRUB_SERIAL_COMMAND="serial --speed=38400 --unit=0 --parity=no --stop=1"' + grub_cfg = os.path.join(rootdir, 'etc', 'default', 'grub') + logging.debug("Allowing serial output in grub config %s", grub_cfg) + with open(grub_cfg, 'a+') as cfg: + cfg.write("# %s serial support\n" % os.path.basename(__file__)) + cfg.write("%s\n" % cmdline) + cfg.write("%s\n" % terminal) + cfg.write("%s\n" % command) + def install_grub2(self, rootdev, rootdir): self.message("Configuring grub2") # rely on kpartx using consistent naming to map loop0p1 to loop0 @@ -561,15 +624,18 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth '%s' % os.path.join(rootdir, 'proc')]) self.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind', '%s' % os.path.join(rootdir, 'sys')]) + if self.settings['serial-console']: + self._grub_serial_console(rootdir) + try: self.runcmd(['chroot', rootdir, 'update-grub']) 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.runcmd(['umount', os.path.join(rootdir, 'sys')]) self.runcmd(['umount', os.path.join(rootdir, 'proc')]) self.runcmd(['umount', os.path.join(rootdir, 'dev')]) - self.install_extlinux(rootdev, rootdir) def install_extlinux(self, rootdev, rootdir): if not os.path.exists("/usr/bin/extlinux"): @@ -600,11 +666,9 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth conf = os.path.join(rootdir, 'extlinux.conf') logging.debug('configure extlinux %s', conf) - # python multiline string substitution is just ugly. - # use an external file or live with the mangling, no point in - # mangling the string to remove spaces just to keep it pretty in source. - f = open(conf, 'w') - f.write(''' + kserial = 'console=ttyS0,115200' if self.settings['serial-console'] else '' + extserial = 'serial 0 115200' if self.settings['serial-console'] else '' + msg = ''' default linux timeout 1 @@ -616,11 +680,16 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s 'kernel': kernel_image, # pylint: disable=bad-continuation 'initrd': initrd_image, # pylint: disable=bad-continuation 'uuid': uuid, # pylint: disable=bad-continuation - 'kserial': # pylint: disable=bad-continuation - 'console=ttyS0,115200' if self.settings['serial-console'] else '', # pylint: disable=bad-continuation - 'extserial': 'serial 0 115200' if self.settings['serial-console'] else '', # pylint: disable=bad-continuation - }) # pylint: disable=bad-continuation - f.close() # pylint: disable=bad-continuation + 'kserial': kserial, # pylint: disable=bad-continuation + 'extserial': extserial, # pylint: disable=bad-continuation + } # pylint: disable=bad-continuation + logging.debug("extlinux config:\n%s", msg) + + # python multiline string substitution is just ugly. + # use an external file or live with the mangling, no point in + # mangling the string to remove spaces just to keep it pretty in source. + f = open(conf, 'w') + f.write(msg) self.runcmd(['extlinux', '--install', rootdir]) self.runcmd(['sync']) @@ -684,7 +753,10 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s self.message('Running customize script %s' % script) logging.info("rootdir=%s image=%s", rootdir, self.settings['image']) with open('/dev/tty', 'w') as tty: - cliapp.runcmd([script, rootdir, self.settings['image']], stdout=tty, stderr=tty) + try: + cliapp.runcmd([script, rootdir, self.settings['image']], stdout=tty, stderr=tty) + except IOError: + subprocess.call([script, rootdir, self.settings['image']]) def create_tarball(self, rootdir): # Create a tarball of the disk's contents @@ -694,16 +766,20 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s def chown(self): # Change image owner after completed build + if self.settings['image']: + filename = self.settings['image'] + elif self.settings['tarball']: + filename = self.settings['tarball'] + else: + return self.message("Changing owner to %s" % self.settings["owner"]) - subprocess.call(["chown", - self.settings["owner"], - self.settings["image"]]) + subprocess.call(["chown", self.settings["owner"], filename]) def list_installed_pkgs(self, rootdir): # output the list of installed packages for sources identification self.message("Creating a list of installed binary package names") out = self.runcmd(['chroot', rootdir, - 'dpkg-query', '-W' "-f='${Package}.deb\n'"]) + 'dpkg-query', '-W', "-f='${Package}.deb\n'"]) with open('dpkg.list', 'w') as dpkg: dpkg.write(out) @@ -723,6 +799,8 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s line = '#deb-src %s %s main\n' % (mirror, self.settings['distribution']) f.write(line) f.close() + # ensure the apt sources have valid lists + self.runcmd(['chroot', rootdir, 'apt-get', '-qq', 'update']) if __name__ == '__main__': VmDebootstrap(version=__version__).run()