Cloud Images

Custom slackware installations have been discused multiple times in forums and publications. In this article I am going to revisit the topic with the aim to generate a slackware image usable on OpenStack based clouds.

This procedure is fully automated and has been tested on a regular slackware installation. Also the resulting image has been deployed to OVH Cloud.

Pre-requisites

This procedure has been developed on a Slackware64 Current machine with a full installation with the only adition of QEMU 4.1, with the Linux kernel 5.4.3.

The procedure does not use any software beyond QEMU and the kernel modules ndb and kvm .

Obtaining Slackware

We need a local copy of the installation files from ◊slackware . We have downloaded a copy of Slackware 64 Current in /mnt/mirrors/slackware/slackware64-current

Another way of downloading ◊slackware is to download a current ISO (built by AlienBob), and mounting it on the same directory.

Disk image

Most public clouds use 10Gb as its smallest disk size, and then grow it to fill the whole disk depending on the flavour of cloud instance you choose. There are multiple ways and tools to build a disk image. The easiest one is to use the qemu-img tool, which will enable us to create a sparse image file using the qcow format.

diskimage-builder is a tool for automatically building customized operating-system images for use in clouds and other environments. It has an extesinve documentation on the OpenStack site.

Because we want to minimize the amount of tools needed to build the image, we are going to use the QEMU utilities.

Partitions and filesystem

To create partitions and filesystems on the image we just created we need to use a virtual device, which will create a block device with the image file as its representation. QEMU Disk Network Block Device Server (ndb) presents QEMU images as block devices which can be used as disks on a Linux system.

To create the partitions into the new created device /dev/ndb0, we can use standard tools like fdisk or sfdisk . We’re going to create a single partition for the root filesystem. We won’t create any data or swap partitions as they can be created later if they are needed in other attached disks.

Using sfdisk , we created a single linux partiton with the following commands:

After that, we will have a new partition /dev/nbd0p1 available, and we can proceed to create the ext4 filesystem, which is the most supported filesystem for Linux systems in the cloud. But we can use others like btrfs if preferred.

The last step is to mount the partition to be able to install the software:

Tag files

Slackware package management tools can be instructed about the priority of a package being installed: ADD, REC, OPT and SKIP .

So if installpkg is invoked with the --menu option, it will take into account package priority levels, which are found in a a file called tagfile in the same folder as the packages to install.

These files can be created to automate ◊slackware package selection when installaing it by setting the priority ADD to those packages we want to install and SKP in the packages we don’t want. The REC and OPT prority levels will instruct the package management tools to ask the user about what to do, so in our automated install we’re going to aboud those. See the ◊slackware book section 18.

A tagfile is created by adding the pacakges and their priorities with the following format:

Our tagfile will contain all the packages available in the slackware insatllation media, with its priorities set up to SKP .

Then we need a file with a list the packages we want to install, one per line:

Then, we need to switch the priority of of the packages listed in out file in the tagfile we created in /tmp/tagfile

Let’s examine the awk code:

The scripts read the file with the selected packages server.template. Then reads the standard input with all the packages available in the distribution and switch the priority to ADD on each package found on our list TMPL, while the rest remains with priority SKP .

Installation

We use the tool installpkg to install ◊slackware, with the pacakge selection specified in the tagfile we created, into our disk image, mounted at /mnt/hd .

After this, the system is installed, but it won’t boot or work properly, until we do the initial system configuration.

System Configuration

To configure the system we will need to execute command from the installed system, using the same installed tools, for that we need to prepare the chroot environment:

We need to create an initrd image containing the virtual IO drivers to boot the machine:

We need to create the fstab file to mount the root partition:

We need to enable DHCP for the first network interface, so when the machine boots, it is accesible using SSH .

Also we need to set the timezone. We select UTC. Enabling the use of a network time server is advisable.

Boot loader

Because installing lilo in a virtual device requires too much fiddle, we installed grub. Note grub executables are installed in the image itself and not in the host system.

Testing the image

We can test the image using QEMU after we umount our disk image:

Automate the process

The creation of our tagfile cannot be automated, but the rest of the procedure can be:

  #!/bin/bash

  # set -e

  IMAGE="/tmp/slack.qcow"
  SIZE="10G"
  DEST=/mnt/hd
  SRC="/mnt/slack"
  TEMPLATE="$HOME/slack/server.template"
  TAGFILE="/tmp/server.tagfile"

  # create an image
  if [ ! -f $IMAGE ]; then
    echo Creating disk image $IMAGE of $SIZE
    qemu-img create -f qcow2 $IMAGE $SIZE
  fi

  # mount image
  if [ ! -b /dev/nbd0 ]; then
    echo Loading NBD driver
    modprobe nbd
  fi

  if [ "$(cat /sys/class/block/nbd0/size)" = "0" ]; then
    echo Attaching $IMAGE to /dev/nbd0 device
    qemu-nbd -c /dev/nbd0 $IMAGE
  fi

  # create partitions
  if [ ! -b /dev/nbd0p1 ]; then
    echo Creating partitions in /dev/nbd0
    echo 'type=83' | sudo sfdisk /dev/nbd0
  fi

  # create filesystem
  if [ "$(file -sL /dev/nbd0p1)" = "/dev/nbd0p1: data" ]; then
    echo Creating EXT4 filesystem into /dev/nbd0p1
      /sbin/mkfs.ext4 /dev/nbd0p1
  fi

  # mount disk image
  if ! grep -q /dev/nbd0p1 /proc/mounts; then
    echo Mounting /dev/nbd0p1 into $DEST
    mount /dev/nbd0p1 $DEST
  fi

  # create single tagfile
  if [ ! -d $TAGDIR ]; then
    echo Creating $TAGDIR with contents of $TAGFILE
      cat /mnt/slack/slackware64/*/tagfile | sed s/:.*/:SKP/g > /tmp/tagfile
      cat /tmp/tagfile | awk 'BEGIN{ while((getline pkg < ARGV[1]) >0) { TMPL[pkg]="ADD" }; close("server.template"); FS=":";}{ if ($1 in TMPL) { printf("%s:ADD\n",$1) } else { print }  }' $TEMPLATE > $TAGFILE
  fi

  # install selected packages
  if [ ! -d /mnt/hd/boot ]; then
    echo "Installing slackware from $SRC/slackware64 into /mnt/hd using template from $TAGFILE"
    sudo /sbin/installpkg --root /mnt/hd --tagfile $TAGFILE --terse --infobox $SRC/slackware64/*/*.t?z
  fi

  # prepare chroot environment
  mount -o bind /proc /mnt/hd/proc
  mount -o bind /dev /mnt/hd/dev
  mount -o bind /sys /mnt/hd/sys

  # update linker symbols
  chroot /mnt/hd /sbin/ldconfig

  # create fstab

  if [ ! -f /mnt/hd/etc/fstab ]; then
    cat << EOF > /mnt/hd/etc/fstab
  /dev/vda1  /           ext4    defaults            1   1
  devpts     /dev/pts    devpts  gid=5,mode=620      0   0
  proc       /proc       proc    defaults            0   0
  tmpfs      /dev/shm    tmpfs   nosuid,nodev,noexec 0   0
  EOF
  fi

  # create initrd image to boot the system
  if [ ! -f /mnt/hd/boot/initrd.gz ]; then
    kernel=$(ls /mnt/hd/boot/ | grep vmlinuz-generic- | cut -f3 -d"-")
    chroot /mnt/hd /sbin/mkinitrd -c -k $kernel -f ext4 \
    -r /dev/vda1 -m virtio_blk:virtio_pci:virtio_balloon:\
    virtio_net:virtio_mmio:virtio_input:virtio_console:\
    virtio_scsi:virtio-rng:virtio-gpu:ext4\
     -u -o /boot/initrd.gz
  fi

  # Set up networking
  grep -q USE_DHCP\[0\]\=\"yes\" /mnt/hd/etc/rc.d/rc.inet1.conf
  if [ $? != 0 ]; then
    sed -i 's/USE_DHCP\[0\]=""/USE_DHCP[0]="yes"/' /mnt/hd/etc/rc.d/rc.inet1.conf
  fi

  cat > /mnt/hd/etc/networks << EOF3
  loopback        127.0.0.0
  localnet        127.0.0.0
  EOF3

  cat > /mnt/hd/etc/HOSTNAME << EOF4
  darkstar.slackware.es
  EOF4

  cat > /mnt/hd/etc/hosts << EOF5
  127.0.0.1 localhost
  127.0.0.1 darkstar.slackware.es darkstar
  EOF5

  # set up the time zone as UTC
  cp /mnt/hd/usr/share/zoneinfo/UTC /mnt/hd/etc/localtime
  echo "UTC" > /mnt/hd/etc/hardwareclock

  # install the boot loader grub
  chroot /mnt/hd /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg
  chroot /mnt/hd /usr/sbin/grub-install --target i386-pc /dev/nbd0

  # set the root password
  echo "root:darkstar" | chroot /mnt/hd /usr/sbin/chpasswd