import crypt
import logging
import os
+import glob
import re
import sys
import shutil
from distro_info import DebianDistroInfo, UbuntuDistroInfo
-__version__ = '0.10'
+__version__ = '0.11'
# pylint: disable=invalid-name,line-too-long,missing-docstring,too-many-branches
def setup_kpartx(self):
bootindex = None
swapindex = None
- out = self.runcmd(['kpartx', '-avs', self.settings['image']])
+ if 'freebsd' in os.sys.platform:
+ out = self.runcmd(['mdconfig', '-a', '-t', 'vnode', '-f',
+ self.settings['image']])
+ else:
+ out = self.runcmd(['kpartx', '-avs', self.settings['image']])
if self.settings['bootsize'] and self.settings['swap'] > 0:
bootindex = 0
rootindex = 1
parts = 1
boot = None
swap = None
- devices = [line.split()[2]
- for line in out.splitlines()
- if line.startswith('add map ')]
+ if 'freebsd' in os.sys.platform:
+ devices = glob.glob("/dev/%ss*" % out.strip())
+ else:
+ devices = ['/dev/mapper/%s' % 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", self.runcmd(['losetup', '-a']))
logging.debug("%s: devices=%s parts=%s", msg, devices, parts)
raise cliapp.AppException(msg)
- root = '/dev/mapper/%s' % devices[rootindex]
+ root = devices[rootindex]
if self.settings['bootsize'] or self.settings['use-uefi']:
- boot = '/dev/mapper/%s' % devices[bootindex]
+ boot = devices[bootindex]
if self.settings['swap'] > 0:
- swap = '/dev/mapper/%s' % devices[swapindex]
+ swap = devices[swapindex]
return root, boot, swap
def _efi_packages(self):
args.append('--foreign')
if self.settings['debootstrapopts']:
for opt in self.settings['debootstrapopts']:
- args.append('--%s' % opt)
+ for part in opt.split(' '):
+ args.append('--%s' % part)
elif self.settings['variant']:
args.append('--variant')
args.append(self.settings['variant'])
def create_fstab(self, rootdir, rootdev, roottype, bootdev, boottype): # pylint: disable=too-many-arguments
def fsuuid(device):
- out = self.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
- '-s', 'UUID', device])
- return out.splitlines()[0].strip()
+ if 'freebsd' in os.sys.platform:
+ out = self.runcmd(['grub-probe', '-d', device, '-t', 'fs_uuid'])
+ return "/dev/ufsid/%s" % out.strip()
+ else:
+ out = self.runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
+ '-s', 'UUID', device])
+ return "UUID=%s" % out.splitlines()[0].strip()
if rootdev:
- rootdevstr = 'UUID=%s' % fsuuid(rootdev)
+ rootdevstr = fsuuid(rootdev)
else:
rootdevstr = '/dev/sda1'
if bootdev and not self.settings['use-uefi']:
- bootdevstr = 'UUID=%s' % fsuuid(bootdev)
+ bootdevstr = 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 'freebsd' in os.sys.platform:
+ f.write('proc /proc linprocfs rw 0 0\n')
+ f.write('sys /sys linsysfs rw 0 0\n')
+ f.write('fdesc /dev/fd fdescfs rw 0 0\n')
+ else:
+ f.write('proc /proc proc defaults 0 0\n')
+ f.write('%s / %s rw 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:
else:
logging.debug('not removing non-existent %s', pathname)
+ def mask_udev_predictable_rules(self, rootdir):
+ """
+ This can be reset later but to get networking working immediately
+ on boot, the interface we're going to use must be known without
+ reference to the eventual machine.
+ http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
+ """
+ self.message('Disabling systemd predictable interface names')
+ udev_path = os.path.join(
+ 'etc', 'udev', 'rules.d', '80-net-setup-link.rules')
+ self.runcmd(['chroot', rootdir, 'ln', '-s', '/dev/null', udev_path])
+
def setup_networking(self, rootdir):
self.message('Setting up networking')
+ ifc_file = os.path.join(rootdir, 'etc', 'network', 'interfaces')
+ ifc_d = os.path.join(rootdir, 'etc', 'network', 'interfaces.d')
- # unconditionally write for wheezy (which became oldstable on 04/25/2015)
+ # unconditionally write for wheezy (which became oldstable 2015.04.25)
if self.was_oldstable(datetime.date(2015, 4, 26)):
- with open(os.path.join(rootdir, 'etc', 'network', 'interfaces'), 'w') as netfile:
+ with open(ifc_file, 'w') as netfile:
netfile.write('source /etc/network/interfaces.d/*\n')
- os.mkdir(os.path.join(rootdir, 'etc', 'network', 'interfaces.d'))
-
- elif not os.path.exists(os.path.join(rootdir, 'etc', 'network', 'interfaces')):
- iface_path = os.path.join(rootdir, 'etc', 'network', 'interfaces')
- with open(iface_path, 'w') as netfile:
+ elif not os.path.exists(ifc_file):
+ with open(ifc_file, 'a') as netfile:
netfile.write('source-directory /etc/network/interfaces.d\n')
- ethpath = os.path.join(rootdir, 'etc', 'network', 'interfaces.d', 'setup')
+
+ if not os.path.exists(ifc_d):
+ os.mkdir(ifc_d)
+ ethpath = os.path.join(ifc_d, 'setup')
with open(ethpath, 'w') as eth:
eth.write('auto lo\n')
eth.write('iface lo inet loopback\n')
eth.write('\n')
eth.write('auto eth0\n')
eth.write('iface eth0 inet dhcp\n')
+ # force predictable interface names
+ self.mask_udev_predictable_rules(rootdir)
def append_serial_console(self, rootdir):
if self.settings['serial-console']:
cfg.write("%s\n" % command)
def _mount_wrapper(self, rootdir):
- self.runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
- '%s' % os.path.join(rootdir, 'dev')])
- self.runcmd(['mount', '/proc', '-t', 'proc', '-obind',
- '%s' % os.path.join(rootdir, 'proc')])
- self.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind',
- '%s' % os.path.join(rootdir, 'sys')])
+ if 'freebsd' in os.sys.platform:
+ self.runcmd(['mount', 'dev', '-t', 'devfs',
+ '%s' % os.path.join(rootdir, 'dev')])
+ self.runcmd(['mount', 'proc', '-t', 'linprocfs',
+ '%s' % os.path.join(rootdir, 'proc')])
+ self.runcmd(['mount', 'sys', '-t', 'linsysfs',
+ '%s' % os.path.join(rootdir, 'sys')])
+ else:
+ self.runcmd(['mount', '/dev', '-t', 'devfs', '-obind',
+ '%s' % os.path.join(rootdir, 'dev')])
+ self.runcmd(['mount', '/proc', '-t', 'proc', '-obind',
+ '%s' % os.path.join(rootdir, 'proc')])
+ self.runcmd(['mount', '/sys', '-t', 'sysfs', '-obind',
+ '%s' % os.path.join(rootdir, 'sys')])
def _umount_wrapper(self, rootdir):
self.runcmd(['umount', os.path.join(rootdir, 'sys')])
time.sleep(5)
self.runcmd(['umount', mount_point], ignore_fail=False)
- self.runcmd(['kpartx', '-d', self.settings['image']], ignore_fail=True)
+ if 'freebsd' in os.sys.platform:
+ out = self.runcmd(['mdconfig', '-l', '-f', self.settings['image']])
+ for devid in out.split():
+ self.runcmd(['mdconfig', '-d', '-u', devid],
+ ignore_fail=True)
+ else:
+ self.runcmd(['kpartx', '-d', self.settings['image']], ignore_fail=True)
for dirname in self.remove_dirs:
shutil.rmtree(dirname)
logging.debug(
"%s usage: %s", self.settings['image'],
self.runcmd(['du', self.settings['image']]))
- with open('/dev/tty', 'w') as tty:
- try:
+ try:
+ with open('/dev/tty', 'w') as tty:
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'],
- self.runcmd(['du', self.settings['image']]))
+ except IOError:
+ logging.debug('tty unavailable, trying in headless mode.')
+ subprocess.call([script, rootdir, self.settings['image']])
def create_tarball(self, rootdir):
# Create a tarball of the disk's contents