Tag Archives: libguestfs

FOSDEM lightning talk on libguestfs next Sunday

If you’re around at FOSDEM next Sunday, please drop in at my very short (10-15 minutes!) lightning talk about libguestfs, virt-builder, virt-sparsify and more.

Room: H.2215 (Ferrer)
Day: Sunday 2nd Feb 2014
Start: 16:00

Leave a comment

Filed under Uncategorized

Tip: Set the keyboard layout in virt-builder

Compared to setting the language of a guest setting the keyboard layout of a guest in virt-builder is a relative piece of cake.

In CentOS you can use the nice Perl-based virt-builder file editing feature to edit the appropriate configuration file:

virt-builder centos-6 \
  --edit '/etc/sysconfig/keyboard:
            s/^KEYTABLE=.*/KEYTABLE="uk"/'

Any line in the file matching KEYTABLE=... is replaced by KEYTABLE="uk". This works by running the Perl expression (s/.../.../ above, but full Perl programs are possible) on the guest file (/etc/sysconfig/keyboard) and uploading the result.

Debian is also simple:

virt-builder debian-7 \
  --edit '/etc/default/keyboard:
            s/^XKBLAYOUT=.*/XKBLAYOUT="gb"/'

Fedora requires that we run a program, localectl(1). Unfortunately localectl requires that dbus is running (for good reasons, but in this case it’s unhelpful), so we can’t just use --run-command to run it while building the guest because libguestfs doesn’t run a dbus daemon. Instead we’ve got to schedule the command to run at first boot, like this:

virt-builder fedora-20 \
  --firstboot-command 'localectl set-keymap uk'

Should virt-builder try to hide all this complexity behind a nice, simple --keyboard option? It’s something to consider.

It would be nice if distros could be more standardized too. I’m suspicious of requiring a program to be run in order to change what ought to be a pure configuration file setting. (If your objection is that other programs must be informed when the configuration changes, then dbus could provide a way to monitor when configuration files change, implemented using inotify.)

3 Comments

Filed under Uncategorized

Tip: Set the guest language in virt-builder

The latest virt-builder can now set the default timezone of a guest using a simple flag:

virt-builder debian-7 \
  --timezone Europe/London

I wanted to add a flag for setting the default language, but it’s essentially impossible.

The problem is that, unlike timezone, there’s no configuration file where you just set the language and that’s it. Each Linux distro and each language you want to set requires a unique combination of:

  • Installing fonts packages.
  • Installing and selecting an input method.
  • Setting up locales.
  • Configuring perhaps multiple things.

To show you how complex this is, here’s the technique I finally came up with (after hours and hours of trying) to set a Fedora 20 guest to Japanese:

virt-builder fedora-20 \
  --size 20G \
  --update \
  --install @japanese-support \
  --install @xfce \
  --install xorg-x11-server-Xorg,xorg-x11-drivers,rsyslog \
  --link /usr/lib/systemd/system/graphical.target:/etc/systemd/system/default.target \
  --firstboot-command 'localectl set-locale LANG=ja_JP.utf8' \
  --firstboot-command 'localectl set-keymap jp' \
  --firstboot-command 'systemctl isolate graphical.target'

And the same thing, but for Debian 7 (Wheezy):

pkgs=locales,xfce4,\
ibus,ibus-anthy,\
ttf-sazanami-gothic,ttf-sazanami-mincho,\
fonts-takao-mincho,\
xfonts-intl-japanese,xfonts-intl-japanese-big,\
iceweasel-l10n-ja,manpages-ja

virt-builder debian-7 \
  --size 20G \
  --install $pkgs \
  --edit '/etc/inittab: s,^#([1-9].*respawn.*/sbin/getty.*),$1,' \
  --edit '/etc/locale.gen: s,^#\s*ja,ja,' \
  --write '/etc/default/locale:LANG="ja_JP.UTF-8"' \
  --run-command "locale-gen"

I would expect that both of these methods would be substantially different for other distros, other languages, or even other versions of Fedora or Debian.

Of course you might say it’s virt-builder’s job to hide all this complexity from the user behind a nice little --language option. And I’m sympathetic — we already do that sort of thing for setting hostnames and installing packages. But this seems like a towering problem that I don’t think we can hope to solve.

Dear distros, please make it easy to install new languages!

1 Comment

Filed under Uncategorized

FOSDEM lightning talk on libguestfs

I’m giving a lightning talk at FOSDEM about new features in libguestfs. Given that it’s only 15 minutes long there’s not going to be much I can cover, but that’s enough time for virt-builder to build about 30 virtual machines on my laptop :-)

Leave a comment

Filed under Uncategorized

New in virt tools: virt-diff

virt-diff is a simple tool I wrote yesterday to display the differences between two virtual machine disk images. Some ways you could use this include:

  • Run an installer on a virtual machine and see what files the installer creates or modifies.
  • See what changes a tool like virt-sysprep is making to a disk image.
  • Reverse engineer changes made by config programs like wicd.

It should work on any Linux or Windows disk image, but it’s a bit rough and ready at the moment.

Anyway I’m going to use it to show what changes virt-builder makes to the template when it builds an operating system image.

Run virt-builder to create an OS:

$ virt-builder fedora-19 \
    --root-password password:123456 \
    --edit '/etc/fedora-release: s/Schrödinger/Henry/'
[   1.0] Downloading: http://libguestfs.org/download/builder/fedora-19.xz
[   2.0] Planning how to build this image
[   2.0] Uncompressing
[  16.0] Opening the new disk
[  36.0] Setting a random seed
[  36.0] Setting root password
[  36.0] Editing: /etc/fedora-release
[  36.0] Finishing off
Output: fedora-19.img
Output size: 4.0G
Output format: raw
Total usable space: 1.9G
Free space: 1.2G (65%)

When virt-builder ran, it downloaded or used a Fedora 19 template, which it saved in its cache. Let’s fish out that template:

$ xzcat ~/.cache/virt-builder/fedora-19.1 > fedora-19-original.img

Finally use virt-diff to show the differences (what virt-builder changed compared to the downloaded template):

$ virt-diff -a fedora-19-original.img -A fedora-19.img
= - 0644         41 /etc/fedora-release
@@ -1 +1 @@
-Fedora release 19 (Schrödinger’s Cat)
+Fedora release 19 (Henry’s Cat)
@@ End of diff @@
= - 0000        672 /etc/shadow
@@ -1,4 +1,4 @@
-root:$6$/hQ0mnIY.tM0f/FB$6bjUN2Dophh9VTN3MBeeSHCgAClesYsdLcb/u/8b5sSbmWAgQyUfBPCcHBV6KfbzsfHiIhWtW155b096t31P70:15979:0:99999:7:::
+root:$6$vU.BupZx3RCtreCl$6EJnzCB4MhjnYmNX7sdi//IqysqKzsp2PmtYgykFfzIlIF5gnqPANVXRTFV2CdflyOpBSCIR7rAmv/s4etWHZ/:15979:0:99999:7:::
 bin:*:15863:0:99999:7:::
 daemon:*:15863:0:99999:7:::
 adm:*:15863:0:99999:7:::
@@ End of diff @@
= - 0600          8 /var/lib/random-seed
@@ End of diff @@

As expected, virt-builder set root’s password, edited the file, and set a new random seed (not shown because it’s binary).

1 Comment

Filed under Uncategorized

Install Fedora 20 with virt-builder

Fedora 20 has been released. You can now install it in a VM using virt-builder:

$ virt-builder fedora-20

This creates a file fedora-20.img. You can import this into libvirt by using the command here.

3 Comments

Filed under Uncategorized

Writing a Planner to solve a tricky programming optimization problem

Suppose a monkey is in one corner of a room, a box is in another corner of a room, and a banana is hanging from the ceiling in the middle of the room. The monkey can’t reach the banana without standing on the box, but he first has to move the box under the banana. The problem of how to get a computer to work out that the monkey has to move the box first, then climb on the box second, was solved by Nils Nilsson’s STRIPS system in 1971. STRIPS is now an A.I. standard, and is used in game A.I. and elsewhere.

Suppose you have a disk image template that you want to uncompress, convert to another format, and resize. You can run xzcat, followed by qemu-img convert followed by virt-resize. But virt-resize can also do format conversion, so you don’t need to run qemu-img convert. Unless the user was happy with the original size, in which case qemu-img convert is faster than virt-resize. But what if the original template isn’t compressed and is already in the right format and size? You can just run cp.

How can a computer work out the right sequence of steps to convert the disk image most efficiently? Virt-builder has exactly this problem, and it solves it using a STRIPS-inspired planner.

The STRIPS planner in virt-builder is only 50 lines of code, was easy to write, finds the near optimal plan for almost any user input, and is a useful technique that can be applied to many programming problems. This article will explain how it works. I have changed some of the academic terms and simplified things to make this easier to understand.

First of all I’ll introduce tags on the original template. These define the state of that template:

Input tags: ✚xz ✚template ✚size=4G ✚format=raw

Secondly I’ll set up my goal state:

Goal tags: ❌xz ❌template ✚size=4G ✚format=qcow2

where means the tag MUST NOT exist in the final state.

I want my planner to find me the best path from my input state to my goal. As it can’t go straight from the input to the goal in one step, I have to tell the planner what transitions are possible, using a function:

transitions (input_tags) {
  if ✚xz then {
    you could run 'xzcat'
        which will ❌xz and ❌template;
  }
  else /* no xz tag */ {
    you could run 'virt-resize'
       which will change ✚format and ✚size, and ❌template;
    or:
    you could run 'qemu-img convert'
       which will change ✚format, and ❌template;
    or:
    etc...
  }

  or:
  you could run 'cp'
      which will ❌template;
}

Notice that the transitions function returns a list of all possible transitions from the input state. It’s not judgemental about which one should be taken, although it won’t return impossible transitions (for example, running virt-resize is not possible on xz-compressed files). The actual transitions function also returns a weight for each transition, so that the planner can choose the least expensive plan if there are several plans possible.

The ✚template tag may appear a bit mysterious. It’s there to make sure that the planner always copies the original template, even if the original template already has the desired goal size and format. Since xzcat, virt-resize and qemu-img convert always copy the disk image, they drop the template tag (❌template).

The transitions function in virt-builder can be found here.

The planner does a breadth-first search over the tree of transitions, starting with the input state, finishing when it finds any branch that satisfies the output goals, or when it reaches a maximum depth in which case it gives up (and the user sees an error message).

The planner in virt-builder (50 lines of code) can be found here.

If the planner finds several paths that satisfy the goals, the planner chooses the one with the smallest weight. However my planner is not clever enough to look deeper in the tree to see if a longer path might have a smaller weight (it’s not very likely in virt-builder).

Also my planner is not smart enough to prune bogus paths. For example, if a path runs cp in adjacent steps, then that path should be pruned.

Nevertheless the planner always gets the right result, and it is considerably simpler than the original hand-written code. The old code had become unmaintainable and wasn’t even efficient: it sometimes made unnecessary copies in order to make the code simpler, wasting end-user time. Because of the ease of maintenance I was able to add new functionality: virt-builder can now run qemu-img resize to expand a disk by < 256 MB, a case where virt-resize doesn’t work (previously the user would have got an error).

Applying old academic techniques like this one doesn’t need to be hard and can help with real world problems. I hope this technique helps others with similar optimization problems.

Edit: The Hacker News discussion includes links to alternative solving tools.

8 Comments

Filed under Uncategorized

KVM working on the Cubietruck

ctg

I managed to get KVM working on the Cubietruck last week. It’s not exactly simple, but this post describes in overview how to do it.

(1) You will need a Cubietruck, a CP2102 serial cable, a micro SDHC card, a card reader for your host computer, and a network patch cable (the board supports wifi but it doesn’t work with the newer kernel we’ll be using). Optional: 2.5″ SATA HDD or SSD.

(2) Start with Hans De Goede’s AllWinner remix of Fedora 19, and get that working. It’s important to read his README file carefully.

(3) Build this upstream kernel with this configuration:

make oldconfig
make menuconfig

In menuconfig, enable Large Page Address Extension (LPAE), and then enable KVM in the Virtualization menu.

LOADADDR=0x40008000 make uImage dtbs
make modules
sudo cp arch/arm/boot/uImage /boot/uImage.sunxi-test
sudo cp arch/arm/boot/dts/sun7i-a20-cubietruck.dtb /boot/sun7i-a20-cubietruck.dtb.sunxi-test
sudo make modules_install

Reboot, interrupt u-boot (using the serial console), and type the following commands to load the new kernel:

setenv bootargs console=ttyS0,115200 loglevel=9 earlyprintk ro rootwait root=/dev/mmcblk0p3
ext2load mmc 0 0x46000000 uImage.sunxi-test
ext2load mmc 0 0x4b000000 sun7i-a20-cubietruck.dtb.sunxi-test
env set fdt_high ffffffff
bootm 0x46000000 - 0x4b000000

(4) Build this modified u-boot which supports Hyp mode.

make cubietruck_config
make
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/YOURSDCARD bs=1024 seek=8

Reboot again, use the commands above to boot into the upstream kernel, and if everything worked you should see:

Brought up 2 CPUs
SMP: Total of 2 processors activated.
CPU: All CPU(s) started in HYP mode.
CPU: Virtualization extensions available.

Also /dev/kvm should exist.

(5) Hack QEMU to create Cortex-A7 CPUs using this one-line patch.

Then you should be able to create VMs using libvirt. Note if using libguestfs you will need to use the direct backend (LIBGUESTFS_BACKEND=direct) because of this libvirt bug.

5 Comments

Filed under Uncategorized

Masking systemd services in a guest

In the previous post I told you how to get cloud-init to work in non-cloud environments.

What if you need to disable cloud-init entirely?

With systemd services and guestfish this is easy:

$ guestfish -a disk.img -i \
     ln-sf /dev/null /etc/systemd/system/cloud-init.service

Why not use this opportunity to get rid of tmp-on-tmpfs at the same time:

$ guestfish -a disk.img -i \
     ln-sf /dev/null /etc/systemd/system/tmp.mount

Systemd’s design of mapping services to files also makes it easy to list the available services in a guest:

$ virt-ls -a /tmp/fedora-19.img -R /lib/systemd/system

Leave a comment

Filed under Uncategorized

Creating a cloud-init config disk for non-cloud boots

There are lots of cloud disk images floating around. They are designed to run in clouds where there is a boot-time network service called cloud-init available that provides initial configuration. If that’s not present, or you’re just trying to boot these images in KVM/libvirt directly without any cloud, then things can go wrong.

Luckily it’s fairly easy to create a config disk (aka “seed disk”) which you attach to the guest and then let cloud-init in the guest get its configuration from there. No cloud, or even network, required.

I’m going to use a tool called virt-make-fs to make the config disk, as it’s easy to use and doesn’t require root. There are other tools around, eg. make-seed-disk which do a similar job. (NB: You might hit this bug in virt-make-fs, which should be fixed in the latest version).

I’m also using a cloud image downloaded from the Fedora project, but any cloud image should work.

First I create my cloud-init metadata. This consists of two files. meta-data contains host and network configuration:

instance-id: iid-123456
local-hostname: cloudy

user-data contains other custom configuration (note #cloud-config is
not a comment, it’s a directive to tell cloud-init the format of the file):

#cloud-config
password: 123456
runcmd:
 - [ useradd, -m, -p, "", rjones ]
 - [ chage, -d, 0, rjones ]

(The idea behind this split is probably not obvious, but apparently it’s because the meta-data is meant to be supplied by the Cloud, and the user-data is meant to be supplied by the Cloud’s customer. In this case, no cloud, so we’re going to supply both!)

I put these two files into a directory, and run virt-make-fs to create the config disk:

$ ls
meta-data  user-data
$ virt-make-fs --type=msdos --label=cidata . /tmp/seed.img
$ virt-filesystems -a /tmp/seed.img --all --long -h
Name      Type        VFS   Label   MBR  Size  Parent
/dev/sda  filesystem  vfat  cidata  -    286K  -
/dev/sda  device      -     -       -    286K  -

Now I need to pass some kernel options when booting the Fedora cloud image, and the only way to do that is if I boot from an external kernel & initrd. This is not as complicated as it sounds, and virt-builder has an option to get the kernel and initrd that I’m going to need:

$ virt-builder --get-kernel Fedora-cloud.raw
download: /boot/vmlinuz-3.9.5-301.fc19.x86_64 -> ./vmlinuz-3.9.5-301.fc19.x86_64
download: /boot/initramfs-3.9.5-301.fc19.x86_64.img -> ./initramfs-3.9.5-301.fc19.x86_64.img

Finally I’m going to boot the guest using KVM (you could also use libvirt with a little extra effort):

$ qemu-kvm -m 1024 \
    -drive file=Fedora-cloud.raw,if=virtio \
    -drive file=seed.img,if=virtio \
    -kernel ./vmlinuz-3.9.5-301.fc19.x86_64 \
    -initrd ./initramfs-3.9.5-301.fc19.x86_64.img \
    -append 'root=/dev/vda1 ro ds=nocloud-net'

You’ll be able to log in either as fedora/123456 or rjones (no password), and you should see that the hostname has been set to cloudy.

2 Comments

Filed under Uncategorized