summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST.in5
-rwxr-xr-xbin/vmdebootstrap301
-rw-r--r--setup.py48
-rw-r--r--vmdebootstrap/__init__.py1
-rw-r--r--vmdebootstrap/base.py43
-rw-r--r--vmdebootstrap/codenames.py2
-rw-r--r--vmdebootstrap/extlinux.py17
-rw-r--r--vmdebootstrap/filesystem.py233
-rw-r--r--vmdebootstrap/grub.py10
-rw-r--r--vmdebootstrap/uefi.py11
10 files changed, 396 insertions, 275 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..093823e
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include NEWS
+include vmextract.py
+include vmdebootstrap.8.in
+recursive-include examples *.sh *.txt
+
diff --git a/bin/vmdebootstrap b/bin/vmdebootstrap
index 1bb9e2e..1880701 100755
--- a/bin/vmdebootstrap
+++ b/bin/vmdebootstrap
@@ -234,28 +234,28 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
sys.exit("You need to have root privileges to run this script.")
self.start_ops()
- def _image_preparations(self, rootdir):
+ def _image_preparations(self):
uefi = self.handlers[Uefi.name]
base = self.handlers[Base.name]
- roottype = self.settings['roottype']
+ filesystem = self.handlers[Filesystem.name]
base.create_empty_image()
self.partition_image()
- if self.settings['mbr'] or self.settings['extlinux']:
- self.install_mbr()
- (rootdev, bootdev, swapdev) = self.setup_kpartx()
+ filesystem.install_mbr()
+ filesystem.setup_kpartx()
+ rootdev = filesystem.devices['rootdev']
+ roottype = filesystem.devices['roottype']
+ bootdev = filesystem.devices['bootdev']
if self.settings['swap'] > 0:
self.message("Creating swap space")
- runcmd(['mkswap', swapdev])
- self.mkfs(rootdev, fstype=roottype)
+ runcmd(['mkswap', filesystem.devices['swapdev']])
+ filesystem.mkfs(rootdev, fstype=roottype)
rootdir = self.mount(rootdev)
+ filesystem.devices['rootdir'] = rootdir
if self.settings['use-uefi']:
self.mount(bootdev, uefi.prepare_esp(rootdir, bootdev))
elif bootdev:
- if self.settings['boottype']:
- boottype = self.settings['boottype']
- else:
- boottype = 'ext2'
- self.mkfs(bootdev, fstype=boottype)
+ boottype = self.settings['boottype']
+ filesystem.mkfs(bootdev, fstype=boottype)
self.bootdir = '%s/%s' % (rootdir, 'boot/')
os.mkdir(self.bootdir)
self.mount(bootdev, self.bootdir)
@@ -265,44 +265,46 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
return
grub = self.handlers[GrubHandler.name]
extlinux = self.handlers[ExtLinux.name]
+ base = self.handlers[Base.name]
+ filesystem = self.handlers[Filesystem.name]
if self.settings['use-uefi']:
grub.install_grub_uefi(rootdir)
+ uefi.configure_efi()
+ grub.install_extra_grub_uefi(rootdir)
+ uefi.configure_extra_efi()
elif self.settings['grub']:
- grub.install_grub2(rootdev, rootdir)
+ if not grub.install_grub2(rootdev, rootdir):
+ extlinux.install_extlinux(rootdev, rootdir)
elif self.settings['extlinux']:
extlinux.install_extlinux(rootdev, rootdir)
- self.append_serial_console(rootdir)
- self.optimize_image(rootdir)
- if self.settings['squash']:
- self.squash()
+ base.append_serial_console(rootdir)
+ base.optimize_image(rootdir)
+ filesystem.squash()
def start_ops(self):
base = self.handlers[Base.name]
- rootdir = None
- rootdev = None
- roottype = self.settings['roottype']
- bootdev = None
- boottype = None
+ filesystem = self.handlers[Filesystem.name]
+ rootdir = filesystem.devices['rootdir']
+ rootdev = filesystem.devices['rootdev']
try:
if self.settings['image']:
- self._image_preparations(rootdir)
+ self._image_preparations()
else:
rootdir = self.mkdtemp()
self.debootstrap(rootdir)
- self.set_hostname(rootdir)
- self.create_fstab(rootdir, rootdev, roottype, bootdev, boottype)
+ filesystem.set_hostname()
+ filesystem.create_fstab()
self.install_debs(rootdir)
base.set_root_password(rootdir)
base.create_users(rootdir)
- self.remove_udev_persistent_rules(rootdir)
+ filesystem.remove_udev_persistent_rules()
self.setup_networking(rootdir)
- if self.settings['configure-apt'] or self.settings['apt-mirror']:
- self.configure_apt(rootdir)
- self.customize(rootdir)
+ filesystem.configure_apt()
+ base.customize(rootdir)
cleanup_apt_cache(rootdir)
- self.update_initramfs(rootdir)
+ filesystem.update_initramfs()
self._image_operations(rootdir, rootdev)
- self.list_installed_pkgs(rootdir)
+ filesystem.list_installed_pkgs()
if self.settings['foreign']:
os.unlink(
@@ -311,7 +313,7 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
if self.settings['tarball']:
base.create_tarball(rootdir)
- self.chown()
+ filesystem.chown()
except BaseException as e:
self.message('EEEK! Something bad happened...')
@@ -421,71 +423,6 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
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')
- if os.path.exists(os.path.join(rootdir, cmd)):
- self.message("Updating the initramfs")
- runcmd(['chroot', rootdir, cmd, '-u'])
-
- def install_mbr(self):
- if os.path.exists("/sbin/install-mbr"):
- self.message('Installing MBR')
- 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 = runcmd(['kpartx', '-avs', self.settings['image']])
- if self.settings['bootsize'] and self.settings['swap'] > 0:
- bootindex = 0
- rootindex = 1
- swapindex = 2
- parts = 3
- elif self.settings['use-uefi']:
- bootindex = 0
- rootindex = 1
- parts = 2
- elif self.settings['use-uefi'] 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:
- msg = 'Surprising number of partitions - check output of losetup -a'
- logging.debug("%s", 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'] or self.settings['use-uefi']:
- boot = '/dev/mapper/%s' % devices[bootindex]
- 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)
- runcmd(['mkfs', '-t', fstype, device])
-
def _bootstrap_packages(self): # pylint: disable=too-many-branches
uefi = self.handlers[Uefi.name]
distro = self.handlers[Codenames.name]
@@ -564,50 +501,6 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
if self.settings['foreign']:
self._debootstrap_second_stage(rootdir)
- def set_hostname(self, rootdir):
- hostname = self.settings['hostname']
- with open(os.path.join(rootdir, 'etc', 'hostname'), 'w') as f:
- f.write('%s\n' % hostname)
-
- etc_hosts = os.path.join(rootdir, 'etc', 'hosts')
- try:
- with open(etc_hosts, 'r') as f:
- data = f.read()
- with open(etc_hosts, 'w') as f:
- for line in data.splitlines():
- if line.startswith('127.0.0.1'):
- line += ' %s' % hostname
- f.write('%s\n' % line)
- except IOError:
- pass
-
- def create_fstab(self, rootdir, rootdev, roottype, bootdev, boottype): # pylint: disable=too-many-arguments
- def fsuuid(device):
- out = runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
- '-s', 'UUID', device])
- return out.splitlines()[0].strip()
-
- if rootdev:
- rootdevstr = 'UUID=%s' % fsuuid(rootdev)
- else:
- rootdevstr = '/dev/sda1'
-
- if bootdev and not self.settings['use-uefi']:
- bootdevstr = 'UUID=%s' % fsuuid(bootdev)
- else:
- bootdevstr = None
-
- 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 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']:
return
@@ -627,16 +520,6 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
logging.debug('stdout:\n%s', out)
shutil.rmtree(tmp)
- def remove_udev_persistent_rules(self, rootdir):
- self.message('Removing udev persistent cd and net rules')
- for x in ['70-persistent-cd.rules', '70-persistent-net.rules']:
- pathname = os.path.join(rootdir, 'etc', 'udev', 'rules.d', x)
- if os.path.exists(pathname):
- logging.debug('rm %s', pathname)
- os.remove(pathname)
- else:
- logging.debug('not removing non-existent %s', pathname)
-
def setup_networking(self, rootdir):
self.message('Setting up networking')
distro = self.handlers[Codenames.name]
@@ -660,56 +543,6 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
eth.write('auto eth0\n')
eth.write('iface eth0 inet dhcp\n')
- def append_serial_console(self, rootdir):
- if self.settings['serial-console']:
- serial_command = self.settings['serial-console-command']
- logging.debug('adding getty to serial console')
- inittab = os.path.join(rootdir, 'etc/inittab')
- # to autologin, serial_command can contain '-a root'
- with open(inittab, 'a') as f:
- f.write('\nS0:23:respawn:%s\n' % serial_command)
-
- def optimize_image(self, rootdir):
- """
- Filing up the image with zeros will increase its compression rate
- """
- if not self.settings['sparse']:
- zeros = os.path.join(rootdir, 'ZEROS')
- self.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros, 'bs=1M'])
- runcmd(['rm', '-f', zeros])
-
- def squash(self):
- """
- Run squashfs on the image.
- """
- if not os.path.exists('/usr/bin/mksquashfs'):
- logging.warning("Squash selected but mksquashfs not found!")
- return
- logging.debug(
- "%s usage: %s", self.settings['image'],
- runcmd(['du', self.settings['image']]))
- self.message("Running mksquashfs")
- suffixed = "%s.squashfs" % self.settings['image']
- if os.path.exists(suffixed):
- os.unlink(suffixed)
- msg = runcmd(
- ['mksquashfs', self.settings['image'],
- suffixed,
- '-no-progress', '-comp', 'xz'], ignore_fail=False)
- logging.debug(msg)
- check_size = os.path.getsize(suffixed)
- if check_size < (1024 * 1024):
- logging.warning(
- "%s appears to be too small! %s bytes",
- suffixed, check_size)
- else:
- logging.debug("squashed size: %s", check_size)
- os.unlink(self.settings['image'])
- self.settings['image'] = suffixed
- logging.debug(
- "%s usage: %s", self.settings['image'],
- runcmd(['du', self.settings['image']]))
-
def cleanup_system(self):
# Clean up after any errors.
@@ -731,71 +564,5 @@ class VmDebootstrap(cliapp.Application): # pylint: disable=too-many-public-meth
for dirname in self.remove_dirs:
shutil.rmtree(dirname)
- def customize(self, rootdir):
- script = self.settings['customize']
- if not script:
- return
- if not os.path.exists(script):
- example = os.path.join("/usr/share/vmdebootstrap/examples/", script)
- if not os.path.exists(example):
- self.message("Unable to find %s" % script)
- return
- script = example
- self.message('Running customize script %s' % script)
- logging.info("rootdir=%s image=%s", rootdir, self.settings['image'])
- logging.debug(
- "%s usage: %s", self.settings['image'],
- runcmd(['du', self.settings['image']]))
- with open('/dev/tty', 'w') as tty:
- try:
- cliapp.runcmd([script, rootdir, self.settings['image']], stdout=tty, stderr=tty)
- except IOError:
- subprocess.call([script, rootdir, self.settings['image']])
- logging.debug(
- "%s usage: %s", self.settings['image'],
- runcmd(['du', self.settings['image']]))
-
- def chown(self):
- if not self.settings['owner']:
- return
- # 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"], filename])
-
- def list_installed_pkgs(self, rootdir):
- if not self.settings['pkglist']:
- return
- # output the list of installed packages for sources identification
- self.message("Creating a list of installed binary package names")
- out = runcmd(['chroot', rootdir,
- 'dpkg-query', '-W', "-f='${Package}.deb\n'"])
- with open('dpkg.list', 'w') as dpkg:
- dpkg.write(out)
-
- def configure_apt(self, rootdir):
- # use the distribution and mirror to create an apt source
- self.message("Configuring apt to use distribution and mirror")
- conf = os.path.join(rootdir, 'etc', 'apt', 'sources.list.d', 'base.list')
- logging.debug('configure apt %s', conf)
- mirror = self.settings['mirror']
- if self.settings['apt-mirror']:
- mirror = self.settings['apt-mirror']
- self.message("Setting apt mirror to %s" % mirror)
- os.unlink(os.path.join(rootdir, 'etc', 'apt', 'sources.list'))
- f = open(conf, 'w')
- line = 'deb %s %s main\n' % (mirror, self.settings['distribution'])
- f.write(line)
- line = '#deb-src %s %s main\n' % (mirror, self.settings['distribution'])
- f.write(line)
- f.close()
- # ensure the apt sources have valid lists
- runcmd(['chroot', rootdir, 'apt-get', '-qq', 'update'])
-
if __name__ == '__main__':
VmDebootstrap(version=__version__).run()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..1c59145
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# setup.py
+#
+# Copyright 2015 Neil Williams <codehelp@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from setuptools import setup, find_packages
+
+
+setup(
+ name='vmdebootstrap',
+ version='1.0',
+ description='Bootstrap Debian into a (virtual machine) disk image',
+ author='Neil Williams',
+ author_email='codehelp@debian.org',
+ url='http://git.liw.fi/cgi-bin/cgit/cgit.cgi/vmdebootstrap/',
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)',
+ 'Programming Language :: Python',
+ 'Topic :: System :: Installation/Setup',
+ ],
+ packages=[
+ 'vmdebootstrap',
+ ],
+ package_data={
+ 'vmdebootstrap': ['README', 'COPYING', 'NEWS'],
+ },
+ scripts=['bin/vmdebootstrap']
+)
diff --git a/vmdebootstrap/__init__.py b/vmdebootstrap/__init__.py
index e69de29..8b13789 100644
--- a/vmdebootstrap/__init__.py
+++ b/vmdebootstrap/__init__.py
@@ -0,0 +1 @@
+
diff --git a/vmdebootstrap/base.py b/vmdebootstrap/base.py
index c2799e7..1843d56 100644
--- a/vmdebootstrap/base.py
+++ b/vmdebootstrap/base.py
@@ -44,6 +44,7 @@ def runcmd(argv, stdin='', ignore_fail=False, env=None, **kwargs):
return out
+# FIXME: use contextmanager
def mount_wrapper(rootdir):
runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
'%s' % os.path.join(rootdir, 'dev')])
@@ -131,3 +132,45 @@ class Base(object):
else:
create_user(userpass)
delete_password(rootdir, userpass)
+
+ def customize(self, rootdir):
+ script = self.settings['customize']
+ if not script:
+ return
+ if not os.path.exists(script):
+ example = os.path.join("/usr/share/vmdebootstrap/examples/", script)
+ if not os.path.exists(example):
+ self.message("Unable to find %s" % script)
+ return
+ script = example
+ self.message('Running customize script %s' % script)
+ logging.info("rootdir=%s image=%s", rootdir, self.settings['image'])
+ logging.debug(
+ "%s usage: %s", self.settings['image'],
+ runcmd(['du', self.settings['image']]))
+ with open('/dev/tty', 'w') as tty:
+ try:
+ cliapp.runcmd([script, rootdir, self.settings['image']], stdout=tty, stderr=tty)
+ except IOError:
+ subprocess.call([script, rootdir, self.settings['image']])
+ logging.debug(
+ "%s usage: %s", self.settings['image'],
+ runcmd(['du', self.settings['image']]))
+
+ def optimize_image(self, rootdir):
+ """
+ Filing up the image with zeros will increase its compression rate
+ """
+ if not self.settings['sparse']:
+ zeros = os.path.join(rootdir, 'ZEROS')
+ self.runcmd_unchecked(['dd', 'if=/dev/zero', 'of=' + zeros, 'bs=1M'])
+ runcmd(['rm', '-f', zeros])
+
+ def append_serial_console(self, rootdir):
+ if self.settings['serial-console']:
+ serial_command = self.settings['serial-console-command']
+ logging.debug('adding getty to serial console')
+ inittab = os.path.join(rootdir, 'etc/inittab')
+ # to autologin, serial_command can contain '-a root'
+ with open(inittab, 'a') as ftab:
+ ftab.write('\nS0:23:respawn:%s\n' % serial_command)
diff --git a/vmdebootstrap/codenames.py b/vmdebootstrap/codenames.py
index 045d5b2..25cd94d 100644
--- a/vmdebootstrap/codenames.py
+++ b/vmdebootstrap/codenames.py
@@ -35,6 +35,7 @@ class Codenames(Base):
super(Codenames, self).__init__()
self.debian_info = DebianDistroInfo()
self.ubuntu_info = UbuntuDistroInfo()
+ self.settings = None
def define_settings(self, settings):
self.settings = settings
@@ -58,4 +59,3 @@ class Codenames(Base):
if not self.debian_info.valid(suite):
return False
return suite == self.debian_info.stable(limit)
-
diff --git a/vmdebootstrap/extlinux.py b/vmdebootstrap/extlinux.py
index 7286282..d5861a9 100644
--- a/vmdebootstrap/extlinux.py
+++ b/vmdebootstrap/extlinux.py
@@ -1,4 +1,6 @@
-#!/usr/bin/env python
+"""
+ Wrapper for Extlinux support
+"""
# -*- coding: utf-8 -*-
#
# extlinux.py
@@ -25,6 +27,8 @@ import cliapp
import logging
from vmdebootstrap.base import Base, runcmd
+# pylint: disable=missing-docstring
+
class ExtLinux(Base):
@@ -88,3 +92,14 @@ append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)s
runcmd(['extlinux', '--install', rootdir])
runcmd(['sync'])
time.sleep(2)
+
+ def install_mbr(self):
+ if not self.settings['mbr'] and not self.settings['extlinux']:
+ return
+ if os.path.exists("/sbin/install-mbr"):
+ self.message('Installing MBR')
+ 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)
diff --git a/vmdebootstrap/filesystem.py b/vmdebootstrap/filesystem.py
index e0a16d9..1ccaeed 100644
--- a/vmdebootstrap/filesystem.py
+++ b/vmdebootstrap/filesystem.py
@@ -20,7 +20,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from vmdebootstrap.base import Base
+
+import os
+import cliapp
+import logging
+import subprocess
+from vmdebootstrap.base import Base, runcmd
+
+# pylint: disable=missing-docstring
class Filesystem(Base):
@@ -29,3 +36,227 @@ class Filesystem(Base):
def __init__(self):
super(Filesystem, self).__init__()
+ self.settings = None
+ self.devices = {
+ 'rootdir': None,
+ 'rootdev': None,
+ 'bootdev': None,
+ 'boottype': None,
+ 'roottype': None,
+ 'swapdev': None,
+ }
+
+ def define_settings(self, settings):
+ self.settings = settings
+ self.devices['roottype'] = self.settings['roottype']
+
+ def chown(self):
+ if not self.settings['owner']:
+ return
+ # 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"], filename])
+
+ def update_initramfs(self):
+ rootdir = self.devices['rootdir']
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+ cmd = os.path.join('usr', 'sbin', 'update-initramfs')
+ if os.path.exists(os.path.join(str(rootdir), cmd)):
+ self.message("Updating the initramfs")
+ runcmd(['chroot', rootdir, cmd, '-u'])
+
+ def setup_kpartx(self):
+ bootindex = None
+ swapindex = None
+ out = runcmd(['kpartx', '-avs', self.settings['image']])
+ if self.settings['bootsize'] and self.settings['swap'] > 0:
+ bootindex = 0
+ rootindex = 1
+ swapindex = 2
+ parts = 3
+ elif self.settings['use-uefi']:
+ bootindex = 0
+ rootindex = 1
+ parts = 2
+ elif self.settings['use-uefi'] 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:
+ msg = 'Surprising number of partitions - check output of losetup -a'
+ logging.debug("%s", 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'] or self.settings['use-uefi']:
+ boot = '/dev/mapper/%s' % devices[bootindex]
+ if self.settings['swap'] > 0:
+ swap = '/dev/mapper/%s' % devices[swapindex]
+ self.devices['rootdev'] = root
+ self.devices['bootdev'] = boot
+ self.devices['swap'] = swap
+
+ def mkfs(self, device, fstype):
+ self.message('Creating filesystem %s' % fstype)
+ runcmd(['mkfs', '-t', fstype, device])
+
+ def create_fstab(self):
+ rootdir = self.devices['rootdir']
+ rootdev = self.devices['rootdev']
+ bootdev = self.devices['bootdev']
+ boottype = self.devices['boottype']
+
+ def fsuuid(device):
+ out = runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
+ '-s', 'UUID', device])
+ return out.splitlines()[0].strip()
+
+ if rootdev:
+ rootdevstr = 'UUID=%s' % fsuuid(rootdev)
+ else:
+ rootdevstr = '/dev/sda1'
+
+ if bootdev and not self.settings['use-uefi']:
+ bootdevstr = 'UUID=%s' % fsuuid(bootdev)
+ else:
+ bootdevstr = None
+
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+
+ fstab = os.path.join(str(rootdir), 'etc', 'fstab')
+ with open(fstab, 'w') as fstab:
+ fstab.write('proc /proc proc defaults 0 0\n')
+ fstab.write('%s / %s errors=remount-ro 0 1\n' % (rootdevstr, roottype))
+ if bootdevstr:
+ fstab.write('%s /boot %s errors=remount-ro 0 2\n' % (bootdevstr, boottype))
+ if self.settings['swap'] > 0:
+ fstab.write("/dev/sda3 swap swap defaults 0 0\n")
+ elif self.settings['swap'] > 0:
+ fstab.write("/dev/sda2 swap swap defaults 0 0\n")
+
+ def squash(self):
+ """
+ Run squashfs on the image.
+ """
+ if not self.settings['squash']:
+ return
+ if not os.path.exists('/usr/bin/mksquashfs'):
+ logging.warning("Squash selected but mksquashfs not found!")
+ return
+ logging.debug(
+ "%s usage: %s", self.settings['image'],
+ runcmd(['du', self.settings['image']]))
+ self.message("Running mksquashfs")
+ suffixed = "%s.squashfs" % self.settings['image']
+ if os.path.exists(suffixed):
+ os.unlink(suffixed)
+ msg = runcmd(
+ ['mksquashfs', self.settings['image'],
+ suffixed,
+ '-no-progress', '-comp', 'xz'], ignore_fail=False)
+ logging.debug(msg)
+ check_size = os.path.getsize(suffixed)
+ if check_size < (1024 * 1024):
+ logging.warning(
+ "%s appears to be too small! %s bytes",
+ suffixed, check_size)
+ else:
+ logging.debug("squashed size: %s", check_size)
+ os.unlink(self.settings['image'])
+ self.settings['image'] = suffixed
+ logging.debug(
+ "%s usage: %s", self.settings['image'],
+ runcmd(['du', self.settings['image']]))
+
+ def configure_apt(self):
+ rootdir = self.devices['rootdir']
+ if not self.settings['configure-apt'] or not self.settings['apt-mirror']:
+ return
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+ # use the distribution and mirror to create an apt source
+ self.message("Configuring apt to use distribution and mirror")
+ conf = os.path.join(str(rootdir), 'etc', 'apt', 'sources.list.d', 'base.list')
+ logging.debug('configure apt %s', conf)
+ mirror = self.settings['mirror']
+ if self.settings['apt-mirror']:
+ mirror = self.settings['apt-mirror']
+ self.message("Setting apt mirror to %s" % mirror)
+ os.unlink(os.path.join(str(rootdir), 'etc', 'apt', 'sources.list'))
+ source = open(conf, 'w')
+ line = 'deb %s %s main\n' % (mirror, self.settings['distribution'])
+ source.write(line)
+ line = '#deb-src %s %s main\n' % (mirror, self.settings['distribution'])
+ source.write(line)
+ source.close()
+ # ensure the apt sources have valid lists
+ runcmd(['chroot', rootdir, 'apt-get', '-qq', 'update'])
+
+ def list_installed_pkgs(self):
+ if not self.settings['pkglist']:
+ return
+ rootdir = self.devices['rootdir']
+ # output the list of installed packages for sources identification
+ self.message("Creating a list of installed binary package names")
+ out = runcmd(['chroot', rootdir,
+ 'dpkg-query', '-W', "-f='${Package}.deb\n'"])
+ with open('dpkg.list', 'w') as dpkg:
+ dpkg.write(out)
+
+ def remove_udev_persistent_rules(self):
+ rootdir = self.devices['rootdir']
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+ self.message('Removing udev persistent cd and net rules')
+ for x in ['70-persistent-cd.rules', '70-persistent-net.rules']:
+ pathname = os.path.join(str(rootdir), 'etc', 'udev', 'rules.d', x)
+ if os.path.exists(pathname):
+ logging.debug('rm %s', pathname)
+ os.remove(pathname)
+ else:
+ logging.debug('not removing non-existent %s', pathname)
+
+ def set_hostname(self):
+ rootdir = self.devices['rootdir']
+ hostname = self.settings['hostname']
+ if not rootdir:
+ raise cliapp.AppException("rootdir not set")
+ with open(os.path.join(str(rootdir), 'etc', 'hostname'), 'w') as f:
+ f.write('%s\n' % hostname)
+
+ etc_hosts = os.path.join(str(rootdir), 'etc', 'hosts')
+ try:
+ with open(etc_hosts, 'r') as f:
+ data = f.read()
+ with open(etc_hosts, 'w') as f:
+ for line in data.splitlines():
+ if line.startswith('127.0.0.1'):
+ line += ' %s' % hostname
+ f.write('%s\n' % line)
+ except IOError:
+ pass
diff --git a/vmdebootstrap/grub.py b/vmdebootstrap/grub.py
index 8d55ce1..22d7658 100644
--- a/vmdebootstrap/grub.py
+++ b/vmdebootstrap/grub.py
@@ -69,8 +69,10 @@ class GrubHandler(Base):
except cliapp.AppException as exc:
logging.warning(exc)
self.message("Failed. Is grub2-common installed? Using extlinux.")
- self.install_extlinux(rootdev, rootdir) # FIXME!
+ umount_wrapper(rootdir)
+ return False
umount_wrapper(rootdir)
+ return True
def install_grub_uefi(self, rootdir):
self.message("Configuring grub-uefi")
@@ -86,8 +88,9 @@ class GrubHandler(Base):
self.message(
"Failed to configure grub-uefi for %s" %
self.settings['arch'])
- umount_wrapper(rootdir)
- self.uefi.configure_efi() # FIXME
+ umount_wrapper(rootdir)
+
+ def install_extra_grub_uefi(self, rootdir):
extra = str(arch_table[self.settings['arch']]['extra'])
if extra:
target = arch_table[extra]['target']
@@ -99,5 +102,4 @@ class GrubHandler(Base):
logging.warning(exc)
self.message(
"Failed to configure grub-uefi for %s" % extra)
- self.uefi.configure_extra_efi() # FIXME
umount_wrapper(rootdir)
diff --git a/vmdebootstrap/uefi.py b/vmdebootstrap/uefi.py
index 82bf1eb..1acae14 100644
--- a/vmdebootstrap/uefi.py
+++ b/vmdebootstrap/uefi.py
@@ -24,7 +24,12 @@
import os
import cliapp
import logging
-from vmdebootstrap.base import Base, runcmd
+from vmdebootstrap.base import (
+ Base,
+ runcmd,
+ mount_wrapper,
+ umount_wrapper,
+)
# pylint: disable=missing-docstring
@@ -106,18 +111,22 @@ class Uefi(Base):
so needs to be after grub and kernel already installed.
"""
self.message('Configuring EFI')
+ mount_wrapper()
efi_removable = str(arch_table[self.settings['arch']]['removable'])
efi_install = str(arch_table[self.settings['arch']]['install'])
self.message('Installing UEFI support binary')
self.copy_efi_binary(efi_removable, efi_install)
+ umount_wrapper()
def configure_extra_efi(self):
extra = str(arch_table[self.settings['arch']]['extra'])
if extra:
+ mount_wrapper()
efi_removable = str(arch_table[extra]['removable'])
efi_install = str(arch_table[extra]['install'])
self.message('Copying UEFI support binary for %s' % extra)
self.copy_efi_binary(efi_removable, efi_install)
+ umount_wrapper()
def partition_esp(self):
espsize = self.settings['esp-size'] / (1024 * 1024)