Пример #1
0
 def gen_fstab(self):
     rootdir = self.devices['rootdir']
     if not rootdir:
         raise cliapp.AppException("rootdir not set")
     # https://bbs.archlinux.org/viewtopic.php?id=220215 and
     # https://stackoverflow.com/questions/36379789/python-subprocess-unable-to-escape-quotes
     runcmd("bash -c 'genfstab -U %s >> %s/etc/fstab'" % (rootdir, rootdir),
            shell=True)
Пример #2
0
 def run_extlinux_install(self, rootdir):
     if os.path.exists("/usr/bin/extlinux"):
         self.message('Running extlinux --install')
         runcmd(['extlinux', '--install', rootdir])
         runcmd(['sync'])
         time.sleep(2)
     else:
         msg = "extlinux enabled but /usr/bin/extlinux not found" \
               " - please install the extlinux package."
         raise cliapp.AppException(msg)
Пример #3
0
 def install_mbr(self):
     if not self.settings['mbr'] or 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)
Пример #4
0
 def enable_systemd_resolved(self, rootdir):
     """
     only for unstable or testing, not present in jessie
     """
     self.message('Enabling systemctl-resolved for DNS')
     runcmd(['chroot', rootdir, 'systemctl', 'enable', 'systemd-resolved'])
     runcmd([
         'chroot', rootdir, 'ln', '-sfT',
         '/run/systemd/resolve/resolv.conf', '/etc/resolv.conf'
     ])
Пример #5
0
 def mask_udev_predictable_rules(self, rootdir):
     """
     This can be reset later but to get networking using eth0
     immediately upon boot, the interface we're going to use must
     be known and must update the initramfs after setting up the
     mask.
     """
     self.message('Disabling systemd predictable interface names')
     udev_path = os.path.join('etc', 'udev', 'rules.d',
                              '80-net-setup-link.rules')
     runcmd(['chroot', rootdir, 'ln', '-s', '/dev/null', udev_path])
Пример #6
0
 def set_time_zone(self):
     rootdir = self.devices['rootdir']
     region = self.settings['region']
     city = self.settings['city']
     if not rootdir:
         raise cliapp.AppException("rootdir not set")
     args = "chroot %s ln -sf /usr/share/zoneinfo/%s/%s /etc/localtime" % (
         rootdir, region, city)
     if self.is_arm():
         self.arm_chroot(rootdir, args, shell=True)
     else:
         runcmd(args, shell=True)
Пример #7
0
 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']
     elif self.settings['squash']:
         filename = self.settings['squash']
     else:
         return
     self.message("Changing owner to %s" % self.settings["owner"])
     runcmd(["chown", "-R", self.settings["owner"], filename])
Пример #8
0
 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.decode('utf-8').split()[2] for line in out.splitlines()
         if line.decode('utf-8').startswith('add map ')
     ]
     if len(devices) != parts:
         msg = 'Surprising number of partitions %d:%d- check output of losetup -a' % (
             len(devices), parts)
         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].decode('utf-8')
     if self.settings['swap'] > 0:
         swap = '/dev/mapper/%s' % devices[swapindex]
     self.devices['rootdev'] = root
     self.devices['bootdev'] = boot
     self.devices['swap'] = swap
Пример #9
0
 def set_localization(self):
     rootdir = self.devices['rootdir']
     locale = self.settings['locale']
     lang = self.settings['lang']
     locale_gen_template = self.env.get_template("locale.gen.j2")
     etc_locale_gen = os.path.join(str(rootdir), 'etc', 'locale.gen')
     locale_gen_template.stream(settings_locale=locale).dump(etc_locale_gen)
     args = ['chroot', rootdir, 'locale-gen']
     if self.is_arm():
         self.arm_chroot(rootdir, args)
     else:
         runcmd(args)
     locale_conf_template = self.env.get_template("locale.conf.j2")
     etc_locale_conf = os.path.join(str(rootdir), 'etc', 'locale.conf')
     locale_conf_template.stream(settings_lang=lang).dump(etc_locale_conf)
Пример #10
0
 def convert_image_to_qcow2(self):
     """
     Current images are all prepared as raw
     rename to .raw and let the conversion put the
     original name back
     """
     if not self.settings['convert-qcow2'] or not self.settings['image']:
         return
     self.message('Converting raw image to qcow2')
     tmpname = self.settings['image'] + '.raw'
     os.rename(self.settings['image'], tmpname)
     runcmd([
         'qemu-img', 'convert', '-O', 'qcow2', tmpname,
         self.settings['image']
     ])
Пример #11
0
 def update_initramfs(self):
     rootdir = self.devices['rootdir']
     if not rootdir:
         raise cliapp.AppException("rootdir not set")
     if not os.path.exists(
             os.path.join(rootdir, 'usr', 'sbin', 'update-initramfs')):
         self.message("Error: Unable to run update-initramfs.")
         return
     if 'no-update-initramfs' in self.settings or not self.settings[
             'update-initramfs']:
         return
     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'])
Пример #12
0
def link_uuid(rootdev):
    """
    This is mainly to fix a problem in update-grub where /etc/grub.d/10_linux
    Checks if the $GRUB_DEVICE_UUID exists in /dev/disk/by-uuid and falls
    back to $GRUB_DEVICE if it doesn't.
    $GRUB_DEVICE is /dev/mapper/loopXpY (on docker)
    Creating the symlink ensures that grub consistently uses
    $GRUB_DEVICE_UUID when creating /boot/grub/grub.cfg
    """
    if os.path.exists('/.dockerenv'):
        logging.info("Running in docker container")
        runcmd(['mkdir', '-p', '/dev/disk/by-uuid'])
        uuid = runcmd(
            ['blkid', '-c', '/dev/null', '-o', 'value', '-s', 'UUID', rootdev])
        uuid = uuid.splitlines()[0].strip()
        os.symlink(rootdev, os.path.join('/dev/disk/by-uuid', uuid))
Пример #13
0
 def enable_systemd_networkd(self, rootdir):
     """
     Get networking working immediately on boot, allow any en* interface
     to be enabled by systemd-networkd using DHCP
     https://coreos.com/os/docs/latest/network-config-with-networkd.html
     http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
     """
     self.message('Enabling systemd-networkd for DHCP')
     ethpath = os.path.join(rootdir, 'etc', 'systemd', 'network',
                            '99-dhcp.network')
     with open(ethpath, 'w') as eth:
         eth.write('[Match]\n')
         eth.write('Name=e*\n')  # jessie uses eth*, stretch uses ens*
         eth.write('\n[Network]\n')
         eth.write('DHCP=yes\n')
     runcmd(['chroot', rootdir, 'systemctl', 'enable', 'systemd-networkd'])
Пример #14
0
def unlink_uuid(rootdev):
    """
    Reset the link created with link_uuid.
    """
    if os.path.exists('/.dockerenv'):
        uuid = runcmd(
            ['blkid', '-c', '/dev/null', '-o', 'value', '-s', 'UUID', rootdev])
        uuid = uuid.splitlines()[0].strip()
        os.remove(os.path.join('/dev/disk/by-uuid', uuid))
Пример #15
0
 def install_grub_uefi(self, rootdir):
     ret = True
     self.message("Configuring grub-uefi")
     target = arch_table[self.settings['arch']]['target']
     grub_opts = "--target=%s" % target
     logging.debug("Running grub-install with options: %s", grub_opts)
     mount_wrapper(rootdir)
     try:
         runcmd(['chroot', rootdir, 'update-grub'])
         runcmd(['chroot', rootdir, 'grub-install', grub_opts])
     except cliapp.AppException as exc:
         logging.warning(exc)
         ret = False
         self.message("Failed to configure grub-uefi for %s" %
                      self.settings['arch'])
     finally:
         umount_wrapper(rootdir)
     if not ret:
         raise cliapp.AppException("Failed to install grub uefi")
Пример #16
0
 def install_extra_grub_uefi(self, rootdir):
     ret = True
     extra = arch_table[self.settings['arch']]['extra']
     if extra:
         logging.debug("Installing extra grub support for %s", extra)
         mount_wrapper(rootdir)
         target = arch_table[extra]['target']
         grub_opts = "--target=%s" % target
         self.message("Adding grub target %s" % grub_opts)
         try:
             runcmd(['chroot', rootdir, 'update-grub'])
             runcmd(['chroot', rootdir, 'grub-install', grub_opts])
         except cliapp.AppException as exc:
             logging.warning(exc)
             ret = False
             self.message("Failed to configure grub-uefi for %s" % extra)
         finally:
             umount_wrapper(rootdir)
         if not ret:
             raise cliapp.AppException("Failed to install extra grub uefi")
Пример #17
0
 def install_grub2(self, rootdev, rootdir):
     self.message("Configuring grub2")
     # rely on kpartx using consistent naming to map loop0p1 to loop0
     grub_opts = os.path.join('/dev', os.path.basename(rootdev)[:-2])
     if self.settings['serial-console']:
         grub_serial_console(rootdir)
     logging.debug("Running grub-install with options: %s", grub_opts)
     mount_wrapper(rootdir)
     link_uuid(rootdev)
     try:
         runcmd(['chroot', rootdir, 'update-grub'])
         runcmd(['chroot', rootdir, 'grub-install', grub_opts])
     except cliapp.AppException as exc:
         logging.warning(exc)
         self.message("Failed. Is grub2-common installed? Using extlinux.")
         umount_wrapper(rootdir)
         return False
     unlink_uuid(rootdev)
     umount_wrapper(rootdir)
     return True
Пример #18
0
    def install_extlinux(self, rootdev, rootdir):
        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')
            basenames = os.listdir(dirname)
            logging.debug('find: %s', basenames)
            for basename in basenames:
                if re.search(pattern, basename):
                    return os.path.join('boot', basename)
            raise cliapp.AppException('Cannot find match: %s' % pattern)

        try:
            kernel_image = find('vmlinuz-.*')
            initrd_image = find('initrd.img-.*')
        except cliapp.AppException as exc:
            self.message("Unable to find kernel. Not installing extlinux.")
            logging.debug("No kernel found. %s. Skipping install of extlinux.", exc)
            return

        out = runcmd(['blkid', '-c', '/dev/null', '-o', 'value',
                      '-s', 'UUID', rootdev])
        uuid = out.splitlines()[0].strip()

        conf = os.path.join(rootdir, '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 ''
        msg = '''
default linux
timeout 1

label linux
kernel %(kernel)s
append initrd=%(initrd)s root=UUID=%(uuid)s ro %(kserial)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
            '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.
        ext_f = open(conf, 'w')
        ext_f.write(msg)
Пример #19
0
 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:")
     args = ['chroot', rootdir, 'pacman', '-Qqe']
     if self.is_arm():
         out = self.arm_chroot(rootdir, args)
         self.message(out.decode("utf-8"))
     else:
         out = runcmd(args)
         with open('pkg.list', 'w') as pkg:
             pkg.write(out)
Пример #20
0
 def upgrade_rootfs(self, rootdir):
     self.message("Updating resolv.conf")
     etc_resolv_conf_template = self.env.get_template("resolv.conf.j2")
     etc_resolf_conf = os.path.join(str(rootdir), 'etc', 'resolv.conf')
     etc_resolv_conf_template.stream(
         settings_nameserver="8.8.8.8").dump(etc_resolf_conf)
     self.message("Upgrading rootfs mounted at %s" % rootdir)
     # Perform pacman upggrade
     args = ['chroot', rootdir, 'pacman', '-Syu', '--noconfirm']
     if self.is_arm():
         self.arm_chroot(rootdir, args)
         # Re-install _e_v_e_r_y_t_h_i_n_g_ to fix any installs that didn't run - mainly triggers when pacstrap was
         # invoked
         args = "chroot %s /bin/bash -c 'pacman -Qnq | pacman -S --noconfirm -'" % rootdir
         self.message("Performing extra step for ARM based rootfs")
         self.arm_chroot(rootdir, args, shell=True)
     else:
         runcmd(args)
     self.message("Updating resolv.conf")
     etc_resolv_conf_template = self.env.get_template("resolv.conf.j2")
     etc_resolv_conf = os.path.join(str(rootdir), 'etc', 'resolv.conf')
     etc_resolv_conf_template.stream(
         settings_nameserver="<enter DNS IP>").dump(etc_resolv_conf)
Пример #21
0
 def process_args(self, args):
     """
     Optionally unpack a tarball of the disk's contents,
     shell out to runcmd since it more easily handles rootdir.
     Then run the vmpacstrap Filesystem squash_rootfs.
     """
     if self.settings['directory'] and self.settings['tarball']:
         raise cliapp.AppException(
             'tarball and directory cannot be used together.')
     if not self.settings['directory'] and not self.settings['tarball']:
         raise cliapp.AppException('Specify either directory or a tarball.')
     try:
         self.filesystem.define_settings(self.settings)
         if self.settings['tarball']:
             # unpacking tarballs containing device nodes needs root
             if os.geteuid() != 0:
                 sys.exit(
                     "You need to have root privileges to unpack the tarball."
                 )
             rootdir = tempfile.mkdtemp()
             self.remove_dirs.append(rootdir)
             logging.debug('mkdir %s', rootdir)
             self.message('Unpacking tarball of disk contents')
             self.filesystem.devices['rootdir'] = rootdir
             runcmd(['tar', '-xf', self.settings['tarball'], '-C', rootdir])
         else:
             self.message("Using %s directory" % self.settings['directory'])
             self.filesystem.devices['rootdir'] = self.settings['directory']
         self.filesystem.squash_rootfs()
     except BaseException as e:
         self.message('EEEK! Something bad happened...')
         self.message(e)
         self.cleanup_system()
         raise
     else:
         self.cleanup_system()
Пример #22
0
 def partition_esp(self):
     if not self.settings['use-uefi']:
         return
     espsize = self.settings['esp-size'] / (1024 * 1024)
     self.message("Using ESP size: %smib %s bytes" %
                  (espsize, self.settings['esp-size']))
     runcmd([
         'parted', '-s', self.settings['image'], 'mkpart', 'primary',
         'fat32', '1',
         str(espsize)
     ])
     runcmd(
         ['parted', '-s', self.settings['image'], 'set', '1', 'boot', 'on'])
     runcmd(
         ['parted', '-s', self.settings['image'], 'set', '1', 'esp', 'on'])
Пример #23
0
 def squash_rootfs(self):
     """
     Run squashfs on the rootfs within the image.
     Copy the initrd and the kernel out, squashfs the rest.
     Also UEFI files, if enabled, ESP partition as a vfat image. TBD.
     """
     if not self.settings['squash']:
         return
     if not os.path.exists('/usr/bin/mksquashfs'):
         logging.warning("Squash selected but mksquashfs not found!")
         return
     if not os.path.exists(self.settings['squash']):
         os.makedirs(self.settings['squash'])
     suffixed = os.path.join(self.settings['squash'], "filesystem.squashfs")
     if os.path.exists(suffixed):
         os.unlink(suffixed)
     _, exclusions = tempfile.mkstemp()
     with open(exclusions, 'w') as exclude:
         exclude.write("/proc\n")
         exclude.write("/dev\n")
         exclude.write("/sys\n")
         exclude.write("/run\n")
     self.message("Running mksquashfs on rootfs.")
     msg = runcmd([
         'nice', 'mksquashfs', self.devices['rootdir'], suffixed,
         '-no-progress', '-comp', 'xz', '-e', exclusions
     ],
                  ignore_fail=False)
     os.unlink(exclusions)
     logging.debug(msg)
     check_size = os.path.getsize(suffixed)
     logging.debug("Created squashfs: %s", 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)
     bootdir = os.path.join(self.devices['rootdir'], 'boot')
     # copying the boot/* files
     self.message("Copying boot files out of squashfs")
     copy_files(bootdir, self.settings['squash'])
Пример #24
0
 def mkfs(self, device, fstype, opt=None):
     self.message('Creating filesystem %s' % fstype)
     if opt:
         runcmd(['mkfs', '-t', fstype, '-O', opt, device])
     else:
         runcmd(['mkfs', '-t', fstype, device])
Пример #25
0
 def make_rootfs_part(self, extent):
     bootsize = self.settings['esp-size'] / (1024 * 1024) + 1
     runcmd([
         'parted', '-s', self.settings['image'], 'mkpart', 'primary',
         str(bootsize), extent
     ])