X-Git-Url: https://git.siccegge.de//index.cgi?p=forks%2Fvmdebootstrap.git;a=blobdiff_plain;f=vmdebootstrap;h=add3d44165bfd3b1ea14a8a52fb79709b787f8c9;hp=ed4904088123b3c3afdb375dd01fb71e23e22183;hb=9904951b643e62d6c36d60132e0e0f56858986d0;hpb=e0cee06cae390afd614e13ada5e12becfb6927ed diff --git a/vmdebootstrap b/vmdebootstrap index ed49040..add3d44 100755 --- a/vmdebootstrap +++ b/vmdebootstrap @@ -22,14 +22,16 @@ import logging import os import re import shutil +import datetime import subprocess import tempfile import time +from distro_info import DebianDistroInfo, UbuntuDistroInfo -__version__ = '0.6' +__version__ = '0.8' -# pylint: disable=invalid-name +# pylint: disable=invalid-name,line-too-long,missing-docstring,too-many-branches class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-methods @@ -38,6 +40,8 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth super(VmDebootstrap, self).__init__(progname, version, description, epilog) self.remove_dirs = [] self.mount_points = [] + self.debian_info = DebianDistroInfo() + self.ubuntu_info = UbuntuDistroInfo() def add_settings(self): default_arch = subprocess.check_output( @@ -120,6 +124,10 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth self.settings.boolean( ['no-kernel'], 'do not install a linux package') + self.settings.string( + ['kernel-package'], + 'install PACKAGE instead of the default kernel package', + metavar='PACKAGE') self.settings.boolean( ['enable-dhcp'], 'enable DHCP on eth0') @@ -184,7 +192,11 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if self.settings['image'] and not self.settings['size']: raise cliapp.AppException( 'If disk image is specified, you must give image size.') - + if not self.debian_info.valid(self.settings['distribution']): + if not self.ubuntu_info.valid(self.settings['distribution']): + raise cliapp.AppException( + '%s is not a valid Debian or Ubuntu suite or codename.' + % self.settings['distribution']) rootdir = None try: rootdev = None @@ -197,7 +209,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth if self.settings['mbr'] or self.settings['extlinux']: self.install_mbr() (rootdev, bootdev, swapdev) = self.setup_kpartx() - if self.settings['swap']: + if self.settings['swap'] > 0: self.message("Creating swap space") self.runcmd(['mkswap', swapdev]) self.mkfs(rootdev, fstype=roottype) @@ -248,7 +260,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') @@ -315,7 +327,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth bootsize = 0 extent = '100%' swap = 256 * 1024 * 1024 - if self.settings['swap']: + if self.settings['swap'] > 0: if self.settings['swap'] > swap: swap = self.settings['swap'] else: @@ -333,6 +345,8 @@ 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'])) @@ -347,7 +361,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth 'mkpart', 'primary', '0%', extent]) self.runcmd(['parted', '-s', self.settings['image'], 'set', '1', 'boot', 'on']) - if self.settings['swap']: + if self.settings['swap'] > 0: logging.debug("Creating swap partition") self.runcmd(['parted', '-s', self.settings['image'], 'mkpart', 'primary', 'linux-swap', extent, '100%']) @@ -371,7 +385,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth bootindex = None swapindex = None out = self.runcmd(['kpartx', '-avs', self.settings['image']]) - if self.settings['bootsize'] and self.settings['swap']: + if self.settings['bootsize'] and self.settings['swap'] > 0: bootindex = 0 rootindex = 1 swapindex = 2 @@ -380,7 +394,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth bootindex = 0 rootindex = 1 parts = 2 - elif self.settings['swap']: + elif self.settings['swap'] > 0: rootindex = 0 swapindex = 1 parts = 2 @@ -393,13 +407,14 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth for line in out.splitlines() if line.startswith('add map ')] if len(devices) != parts: - msg = '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] - if self.settings['swap']: + if self.settings['swap'] > 0: swap = '/dev/mapper/%s' % devices[swapindex] return root, boot, swap @@ -407,6 +422,26 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth self.message('Creating filesystem %s' % fstype) self.runcmd(['mkfs', '-t', fstype, device]) + def suite_to_codename(self, distro): + suite = self.debian_info.codename(distro, datetime.date.today()) + if not suite: + return distro + return suite + + def was_oldstable(self, limit): + suite = self.suite_to_codename(self.settings['distribution']) + # this check is only for debian + if not self.debian_info.valid(suite): + return False + return suite == self.debian_info.old(limit) + + def was_stable(self, limit): + suite = self.suite_to_codename(self.settings['distribution']) + # this check is only for debian + if not self.debian_info.valid(suite): + return False + return suite == self.debian_info.stable(limit) + def debootstrap(self, rootdir): msg = "(%s)" % self.settings['variant'] if self.settings['variant'] else '' self.message('Debootstrapping %s %s' % (self.settings['distribution'], msg)) @@ -417,14 +452,23 @@ 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' + if self.settings['kernel-package']: + kernel_image = self.settings['kernel-package'] else: - kernel_arch = self.settings['arch'] - kernel_image = 'linux-image-%s' % kernel_arch + if self.settings['arch'] == 'i386': + # wheezy (which became oldstable on 04/25/2015) used '486' + if self.was_oldstable(datetime.date(2015, 4, 26)): + kernel_arch = '486' + else: + kernel_arch = '586' + elif self.settings['arch'] == 'armhf': + kernel_arch = 'armmp' + else: + kernel_arch = self.settings['arch'] + kernel_image = 'linux-image-%s' % kernel_arch include.append(kernel_image) if self.settings['sudo'] and 'sudo' not in include: @@ -501,9 +545,9 @@ 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']: + if self.settings['swap'] > 0: f.write("/dev/sda3 swap swap defaults 0 0\n") - elif self.settings['swap']: + elif self.settings['swap'] > 0: f.write("/dev/sda2 swap swap defaults 0 0\n") def install_debs(self, rootdir): @@ -596,6 +640,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 @@ -606,15 +663,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"): @@ -732,7 +792,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