Objectives:

• perform a scripted install for a fully-automatic OS installation
• use LVM commands to create and manage logical volumes

Most of the actions in this exercise are done as "root", so if you are not root already type:

$sudo -s # Any set of actions/commands that starts with # is to be run as root and any that starts with a $ should be run as the normal class user.

# 1 Scripted installs

Scripted installs can be used to automate the creation of new VM images, and avoid most of the interactive questions asked during a CD install.

Under Ubuntu, the tool "python-vm-builder" can be used for this; under Debian the equivalent tool is "grml-debootstrap", and this is what you will use in this exercise.

## 1.1 Install grml-debootstrap

# apt-get install grml-debootstrap

The configuration settings are available by reading man grml-debootstrap and by looking in the file /etc/debootstrap/config

# 2 Build a VM image file

grml-debootstrap creates a familiar disk image file, containing an MBR and a partition table, so it can be booted directly. This is the direct equivalent of what we did earlier using virt-manager to install Ubuntu inside our debian hosts.

# grml-debootstrap --vmfile --target /var/lib/libvirt/images/debian1.img \
--hostname debian1 -v \
--mirror http://apt.ws.nsrc.org:3142/cdn.debian.net/debian

This will take a few minutes. It builds a raw image, default size 2GB. If you watch carefully, you will see messages about loopback devices like /dev/mapper/loop0p1. This is explained in the optional exercises at the end.

Near the end of the process you will be prompted for a root password for your new VM.

## 2.1 Check the image

This file is "sparse" - the host operating system has only allocated space for blocks which have been written to. You can see this by comparing the size of the file with the disk space allocated to the file.

# ls -lh /var/lib/libvirt/images/debian1.img      # size of file = 2GB
# ls -sh /var/lib/libvirt/images/debian1.img      # space used < 2GB
# du -h /var/lib/libvirt/images/debian1.img       # another way to get the same info

From our terminology presentation this is the sparse disk image that is supported by some filesystems. We had it illustrated thusly:

However, such a file may lose its "sparseness" when copied: that is, it could grow to take 2GB of disk space, with all the unused blocks being filled with zeros.

## 2.2 Convert the image

If you want the image in a different format, e.g. qcow2, then use qemu-img convert to convert it. This will take a little while as it has to read and rewrite the entire image.

# qemu-img convert -f raw -O qcow2 /var/lib/libvirt/images/debian1.img /var/lib/libvirt/images/debian1.qcow2

qemu-img is part of the qemu-utils package. -f raw selects the input format and -O qcow2 selects the desired output format.

Once it is finished, compare the file sizes:

# du -sh /var/lib/libvirt/images/debian1.*

The two image file sizes may be slightly different.

This qcow2 image is what we illustrated as a growable image file in our terminology presentation:

## 2.3 Start a VM

Finally, to boot this as a VM using libvirt, you need to create a VM inside libvirt and attach the image.

Rather than write the libvirt XML for this all by hand, you could use the virt-manager GUI, but this time you will use the command line tool "virt-install" to create the machine. You pass a number of parameters telling it the parameters of the machine to build.

Hint: if you are inside the X11 graphical environment then it's better to run this as a regular user, not root, so that it can open the graphical console window for you.

Note: the password below is "xyzzy" when prompted for this to connect to the VM once it is started.

$virt-install --name debian1 --ram 512 \ --graphics vnc,listen=0.0.0.0,password=xyzzy \ --import --disk /var/lib/libvirt/images/debian1.qcow2,format=qcow2 \ --network=bridge:br-lan This image has a partition table and Grub so it's very easy to boot. However, since it has a partition table inside the image growing it beyond the initial 2GB specified at creation time is not that easy to do. When your VM is running, move onto the next section about LVM. # 3 LVM The Logical Volume Manager gives an approach to disk space management which is much more flexible than partitioning. First, try each of the following commands to list the logical volumes you have: # lvscan # lvs # lvdisplay Compare the output - it has differing levels of detail but should show the same logical volumes. Have a look at the device nodes: # ls -l /dev/mapper # ls -l /dev/ganeti Now create a new logical volume called "foo" (or some other name you choose) inside the "ganeti" volume group: # lvcreate --size 1G --name foo ganeti Use these commands to check that the device node exists, and to see the size of the block device you have created: # ls -l /dev/ganeti # blockdev --getsize64 /dev/ganeti/foo Now we can create a filesystem within it, and mount it. MAKE SURE YOU DO THESE OPERATIONS ON THE CORRECT DEVICE NODE - if you write to your root, var or swap volumes you will break your system! # mkfs.ext4 /dev/ganeti/foo # mkdir /opt # if /opt already exists, ignore the error # mount /dev/ganeti/foo /opt # df -h /opt You should now be able to create files in /opt if you wish, and they will be stored in this logical volume. # echo "testing this" > /opt/file1.txt # cp /etc/motd /opt # mkdir /opt/bin # cp -p /bin/ls /opt/bin/. # ls -l /opt/* Now let's grow the filesystem. This requires two steps: firstly add some extents to the logical volume. # lvextend --size +1G /dev/ganeti/foo # blockdev --getsize64 /dev/ganeti/foo # df -h /opt You'll see that although the logical volume is bigger, the filesystem is still the same size. You need to resize the filesystem to fill the larger space available. Fortunately, with ext4 you can do this even while the filesystem is mounted: # resize2fs /dev/ganeti/foo # df -h /opt Now let's undo everything. Unmount the filesystem and delete the logical volume. # umount /opt # lvremove /dev/ganeti/foo # lvscan As you can see, creating and destroying logical volumes is a straightforward process. There is plenty of additional LVM documentation online that you can read. From the point of view of a virtual machine, a logical volume behaves much like a raw disk image file, but avoids the overhead of going through the host filesystem. # 4 Build a VM filesystem in a logical volume Since you now know how to create logical volumes, you will create one for this image. We will then install debian into it directly treating the volume as a single straight partition. # lvcreate --size 4G --name debian2 ganeti # grml-debootstrap --target /dev/ganeti/debian2 --hostname debian2 -v \ --mirror http://apt.ws.nsrc.org:3142/cdn.debian.net/debian You will be asked to confirm the settings. If it looks ok, answer y. This will take a few minutes. You will see it installing the packages into the filesystem. After the initial confirmation to proceed, the only questions you are asked are to set a root password. Once finished, you should see:  * Finished execution of grml-debootstrap. Enjoy your Debian system. ## 4.1 Examine the image When built this way, the logical volume contains a single filesystem (with no partition table), and it is easy to mount and examine, which is very useful. # mount /dev/ganeti/debian2 /mnt # ls /mnt Let's make a small change to the filesystem: we will enable the serial console. For a Debian image it's done like this: # vi /mnt/etc/inittab ---- Uncomment this line (remove the '#' in front of it) ---- T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100 Save the change. Finally, unmount the fileystem: # umount /mnt NOTE: to avoid filesystem corruption, it's VERY IMPORTANT that the same block device is not mounted in two places at once. So NEVER mount a volume like this while it is attached to a running VM; and always remember to unmount it before you start a VM which uses it. ## 4.2 Booting the image Now we want to boot the image as a VM. However there is a problem. Because this image is a straight filesystem, without a partition table, it does not contain a Master Boot Record (MBR) and doesn't have the GRUB boot loader. So it won't boot in the way a normal PC does. What we can do instead is to pass KVM two files (a kernel and init ramdisk) stored on the host. KVM will then do the work grub would have done and boot the kernel for us. We will use the same kernel and initrd as is used to boot the Debian host you have built. You will use virt-install as before to set this up. $ virt-install --name debian2 --ram 512 \
--import --disk /dev/ganeti/debian2 --network=bridge:br-lan \
--boot kernel=/vmlinuz,initrd=/initrd.img,kernel_args="root=/dev/sda"

Hint: If you don't get a console, use your laptop's VNC client to connect. virsh vncdisplay debian2 will show you which VNC screen to connect to.

kernel_args="root=/dev/sda" tells the kernel which device to mount as its root filesystem.

You should now have a running VM using the image you built. If not, ask for help. Remember to do some basic debugging first:

# virsh list
# virsh dumpxml debian2
# virsh console debian2

# 5 Optional exercises

## 5.1 Cloning virtual machines

Another approach to creating VMs is to clone one you have already installed. This can be much faster than doing an installation from scratch, but some work may be needed to update the clone before it can be used.

You need to duplicate the disk image or logical volume, create a new VM definition, start it, and then patch up the new VM.

### 5.1.1 Duplicate the image file

This part is easy! Let's say you have a VM backed by "debian1.qcow2", and we want to create "debian3.qcow2"; you simply copy it. Make sure the original VM is shutdown first, so its filesystem is in a consistent state.

# virsh shutdown debian1
# cd /var/lib/libvirt/images
# cp debian1.qcow2 debian3.qcow2
# ls -l

### 5.1.2 Create a new VM definition

We will take the libvirt XML definition of debian1, and make some minimal changes to make a machine called debian3 with the same parameters.

# cd
# virsh dumpxml debian1 >debian3.xml
# vi debian3.xml

-- CHANGE the name
<name>debian3</name>

-- DELETE the <uuid>...</uuid> line

-- CHANGE the source file line
<source file='/var/lib/libvirt/images/debian3.qcow2'/>

-- DELETE the <mac address='52:54:xx:xx:xx:xx'/> line

Save and exit from the file.

Now define the new VM from this file (the missing UUID and MAC address should be assigned automatically).

# virsh define debian3.xml

If that gives no error, then start your new VM:

# virsh start debian3

### 5.1.3 Patch up the VM

Get a console onto your new VM (using VNC), and you'll find there are some changes you would need to make.

Firstly, this host still thinks its name is "debian1". So you need to edit /etc/hostname, /etc/hosts, /etc/mailname and maybe others.

Secondly, this host may still be configured with the IP address of debian1. In our case we're using DHCP so it's not a problem, but with static addressing you would have to be very careful to avoid conflicts.

Thirdly, this host may still remember the MAC address which used to be on eth0, and so call its primary interface something else (like eth1), because it doesn't recognize the MAC address assigned when we did a virsh define - and thinkg it's a new interface.

If the VM is Ubuntu, check /etc/udev/rules.d/70-persistent-net.rules. If it is CentOS, check for MAC addresses in /etc/sysconfig/network-scripts/ifcfg-eth*

For operating systems like Windows, there may be other things to do such as changing the machine ID in the registry. We do not have time in this workshop to go into details.

In many cases you will have a local cloning script or utility that takes care of these details for you.

## 5.2 Examining and modifying image files

### 5.2.1 Raw images

It would be very useful if we could look inside an image file to see its contents. The "loop" device lets us attach a file as if it were a block device. However it's slightly awkward because the image we have is not just a filesystem - it's an image of a disk containing a partition table and a filesystem within a partition, which is offset from the beginning of the disk.

Try running the file command on the debian1.img file:

# file /var/lib/libvirt/images/debian1.img

/var/lib/libvirt/images/debian1.img: x86 boot sector; partition 1: ID=0x83, starthead 0, startsector 4096, 4190208 sectors, code offset 0x31

This shows it's a PC disk image with a boot sector and partition table.

(Note: you would not mount the disk image of a running VM to avoid corruption)

The 'kpartx' utility is able to set up separate loopback devices for each partition contained within the image:

# kpartx -av /var/lib/libvirt/images/debian1.img
# ls /dev/mapper/loop*

You should now see you have a device /dev/mapper/loop0p1 which is a block device to access the data in partition 1 of the image, and now you can mount the filesystem and look inside:

# mount /dev/mapper/loop0p1 /mnt
# ls /mnt
... do whatever you need inside the /mnt directory

IMPORTANT!! As before, once you have finished using the mount, it's vital to unmount it and deconfigure the loopback device - otherwise you risk corruption if you start running a VM on this image at the same time as the host system has it mounted.

# umount /mnt
# kpartx -d /var/lib/libvirt/images/debian1.img

If you want to learn more about loopback devices, read the documentation for the losetup tool.

### 5.2.2 qcow2 images

qcow2 images need to be mounted in a different way, since they are a custom format with a header and data, and the tool to do this is qemu-nbd (network block device). Be sure you stop your debian1 virtual image before mounting its filesystem:

# virsh list --all

Verify if debian1 is running. If it is, then:

# virsh shutdown debian1

And, now use the ndb module and qemu-nbd:

# modprobe nbd max_part=4
# qemu-nbd -c /dev/nbd0 /var/lib/libvirt/images/debian1.qcow2
# mount /dev/nbd0p1 /mnt
# ls /mnt
... do work here
# umount /mnt
# qemu-nbd -d /dev/nbd0

### 5.2.3 guestfish / libguestfs

Another tool to access the data inside a VM image is 'guestfish' - the shell frontend to libguestfs. Install it, read the documentation and try it out.

# apt-get install guestfish    # say "Yes" to "Install supermin appliance now"
# man guestfish

It works by building a tiny VM (the "supermin appliance") and running it with the disk image attached. It's especially helpful in complex scenarios, such as when the guest disk is a logical volume but the guest is also using LVM inside that.

The related package 'libguestfs-tools' includes further tools such as virt-resize