Modifying images Once you have obtained a virtual machine image, you may want to make some changes to it before uploading it to the OpenStack Image service. Here we describe several tools available that allow you to modify images. Do not attempt to use these tools to modify an image that is attached to a running virtual machine. These tools are designed to only modify images that are not currently running.
guestfish The guestfish program is a tool from the libguestfs project that allows you to modify the files inside of a virtual machine image. Note that guestfish doesn't mount the image directly into the local filesystem. Instead, it provides you with a shell interface that allows you to view, edit, and delete files. Many of the guestfish commands (e.g., touch, chmod, rm) are similar to traditional bash commands. Example guestfish session We often need to modify a virtual machine image to remove any traces of the MAC address that was assigned to the virtual network interface card when the image was first created, since the MAC address will be different when it boots the next time. In this example, we show how we can use guestfish to remove references to the old MAC address by deleting the /etc/udev/rules.d/70-persistent-net.rules file and removing the HWADDR line from the /etc/sysconfig/network-scripts/ifcfg-eth0 file. Assume we have a CentOS qcow2 image called centos63_desktop.img. We would mount the image in read-write mode by doing, as root: # guestfish --rw -a centos63_desktop.img Welcome to guestfish, the libguestfs filesystem interactive shell for editing virtual machine filesystems. Type: 'help' for help on commands 'man' to read the manual 'quit' to quit the shell ><fs>This starts a guestfish session. Note that the guestfish prompt looks like a fish: > <fs>. We must first use the run command at the guestfish prompt before we can do anything else. This will launch a virtual machine, which will be used to perform all of the file manipulations.><fs> run We can now view the filesystems in the image using the list-filesystems command:><fs> list-filesystems /dev/vda1: ext4 /dev/vg_centosbase/lv_root: ext4 /dev/vg_centosbase/lv_swap: swapWe need to mount the logical volume that contains the root partition: ><fs> mount /dev/vg_centosbase/lv_root / Next, we want to delete a file. We can use the rm guestfish command, which works the same way it does in a traditional shell. ><fs> rm /etc/udev/rules.d/70-persistent-net.rulesWe want to edit the ifcfg-eth0 file to remove the HWADDR line. The edit command will copy the file to the host, invoke your editor, and then copy the file back. ><fs> edit /etc/sysconfig/network-scripts/ifcfg-eth0 If you want to modify this image to load the 8021q kernel at boot time, you must create an executable script in the /etc/sysconfig/modules/ directory. You can use the touch guestfish command to create an empty file, the edit command to edit it, and the chmod command to make it executable.><fs> touch /etc/sysconfig/modules/8021q.modules ><fs> edit /etc/sysconfig/modules/8021q.modules We add the following line to the file and save it:modprobe 8021qThen we set to executable: ><fs> chmod 0755 /etc/sysconfig/modules/8021q.modules We're done, so we can exit using the exit command:><fs> exit Going further with guestfish There is an enormous amount of functionality in guestfish and a full treatment is beyond the scope of this document. Instead, we recommend that you read the guestfs-recipes documentation page for a sense of what is possible with these tools.
guestmount For some types of changes, you may find it easier to mount the image's filesystem directly in the guest. The guestmount program, also from the libguestfs project, allows you to do so. For example, to mount the root partition from our centos63_desktop.qcow2 image to /mnt, we can do: # guestmount -a centos63_desktop.qcow2 -m /dev/vg_centosbase/lv_root --rw /mnt If we didn't know in advance what the mountpoint is in the guest, we could use the -i(inspect) flag to tell guestmount to automatically determine what mount point to use:# guestmount -a centos63_desktop.qcow2 -i --rw /mntOnce mounted, we could do things like list the installed packages using rpm:# rpm -qa --dbpath /mnt/var/lib/rpm Once done, we unmount:# umount /mnt
virt-* tools The libguestfs project has a number of other useful tools, including: virt-edit for editing a file inside of an image. virt-df for displaying free space inside of an image. virt-resize for resizing an image. virt-sysprep for preparing an image for distribution (e.g., delete SSH host keys, remove MAC address info, remove user accounts). virt-sparsify for making an image sparse virt-p2v for converting a physical machine to an image that runs on KVM virt-v2v for converting Xen and VMWare images to KVM images Modify a single file inside of an image This example shows how to use virt-edit to modify a file. The command can take either a filename as an argument with the -a flag, or a domain name as an argument with the -d flag. The following examples shows how to use this to modify the /etc/shadow file in instance with libvirt domain name instance-000000e1 that is currently running: # virsh shutdown instance-000000e1 # virt-edit -d instance-000000e1 /etc/shadow # virsh start instance-000000e1 Resize an image Here's a simple of example of how to use virt-resize to resize an image. Assume we have a 16GB Windows image in qcow2 format that we want to resize to 50GB. First, we use virt-filesystems to identify the partitions:# virt-filesystems --long --parts --blkdevs -h -a /data/images/win2012.qcow2 Name Type MBR Size Parent /dev/sda1 partition 07 350M /dev/sda /dev/sda2 partition 07 16G /dev/sda /dev/sda device - 16G - In this case, it's the /dev/sda2 partition that we want to resize. We create a new qcow2 image and use the virt-resize command to write a resized copy of the original into the new image# qemu-img create -f qcow2 /data/images/win2012-50gb.qcw2 50G # virt-resize --expand /dev/sda2 /data/images/win2012.qcow2 /data/images/win2012-50gb.qcow2 Examining /data/images/win2012.qcow2 ... ********** Summary of changes: /dev/sda1: This partition will be left alone. /dev/sda2: This partition will be resized from 15.7G to 49.7G. The filesystem ntfs on /dev/sda2 will be expanded using the 'ntfsresize' method. ********** Setting up initial partition table on /data/images/win2012-50gb.qcow2 ... Copying /dev/sda1 ... 100% ⟦▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓⟧ 00:00 Copying /dev/sda2 ... 100% ⟦▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓⟧ 00:00 Expanding /dev/sda2 using the 'ntfsresize' method ... Resize operation completed with no errors. Before deleting the old disk, carefully check that the resized disk boots and works correctly.
Loop devices, kpartx, network block devices If you don't have access to libguestfs, you can mount image file systems directly in the host using loop devices, kpartx, and network block devices. Mounting untrusted guest images using the tools described in this section is a security risk, always use libguestfs tools such as guestfish and guestmount if you have access to them. See A reminder why you should never mount guest disk images on the host OS by Daniel Berrangé for more details. Mounting a raw image (without LVM) If you have a raw virtual machine image that is not using LVM to manage its partitions. First, use the losetup command to find an unused loop device. # losetup -f /dev/loop0 In this example, /dev/loop0 is free. Associate a loop device with the raw image:# losetup /dev/loop0 fedora17.img If the image only has a single partition, you can mount the loop device directly:# mount /dev/loop0 /mnt If the image has multiple partitions, use kpartx to expose the partitions as separate devices (e.g., /dev/mapper/loop0p1), then mount the partition that corresponds to the root file system:# kpartx -av /dev/loop0 If the image has, say three partitions (/boot, /, /swap), there should be one new device created per partition:$ ls -l /dev/mapper/loop0p* brw-rw---- 1 root disk 43, 49 2012-03-05 15:32 /dev/mapper/loop0p1 brw-rw---- 1 root disk 43, 50 2012-03-05 15:32 /dev/mapper/loop0p2 brw-rw---- 1 root disk 43, 51 2012-03-05 15:32 /dev/mapper/loop0p3To mount the second partition, as root:# mkdir /mnt/image # mount /dev/mapper/loop0p2 /mntOnce you're done, to clean up:# umount /mnt # kpartx -d /dev/loop0 # losetup -d /dev/loop0 Mounting a raw image (with LVM) If your partitions are managed with LVM, use losetup and kpartx as in the previous example to expose the partitions to the host:# losetup -f /dev/loop0 # losetup /dev/loop0 rhel62.img # kpartx -av /dev/loop0 Next, you need to use the vgscan command to identify the LVM volume groups and then vgchange to expose the volumes as devices:# vgscan Reading all physical volumes. This may take a while... Found volume group "vg_rhel62x8664" using metadata type lvm2 # vgchange -ay 2 logical volume(s) in volume group "vg_rhel62x8664" now active # mount /dev/vg_rhel62x8664/lv_root /mnt Clean up when you're done:# umount /mnt # vgchange -an vg_rhel62x8664 # kpartx -d /dev/loop0 # losetup -d /dev/loop0 Mounting a qcow2 image (without LVM) You need the nbd (network block device) kernel module loaded to mount qcow2 images. This will load it with support for 16 block devices, which is fine for our purposes. As root:# modprobe nbd max_part=16 Assuming the first block device (/dev/nbd0) is not currently in use, we can expose the disk partitions using the qemu-nbd and partprobe commands. As root:# qemu-nbd -c /dev/nbd0 image.qcow2 # partprobe /dev/nbd0 If the image has, say three partitions (/boot, /, /swap), there should be one new device created for each partition: $ ls -l /dev/nbd3* brw-rw---- 1 root disk 43, 48 2012-03-05 15:32 /dev/nbd0 brw-rw---- 1 root disk 43, 49 2012-03-05 15:32 /dev/nbd0p1 brw-rw---- 1 root disk 43, 50 2012-03-05 15:32 /dev/nbd0p2 brw-rw---- 1 root disk 43, 51 2012-03-05 15:32 /dev/nbd0p3 If the network block device you selected was already in use, the initial qemu-nbd command will fail silently, and the /dev/nbd3p{1,2,3} device files will not be created. If the image partitions are not managed with LVM, they can be mounted directly:# mkdir /mnt/image # mount /dev/nbd3p2 /mnt When you're done, clean up:# umount /mnt # qemu-nbd -d /dev/g nbd0 Mounting a qcow2 image (with LVM) If the image partitions are managed with LVM, after you use qemu-nbd and partprobe, you must use vgscan and vgchange -ay in order to expose the LVM partitions as devices that can be mounted:# modprobe nbd max_part=16 # qemu-nbd -c /dev/nbd0 image.qcow2 # partprobe /dev/nbd0# vgscan Reading all physical volumes. This may take a while... Found volume group "vg_rhel62x8664" using metadata type lvm2 # vgchange -ay 2 logical volume(s) in volume group "vg_rhel62x8664" now active # mount /dev/vg_rhel62x8664/lv_root /mnt When you're done, clean up:# umount /mnt # vgchange -an vg_rhel62x8664 # qemu-nbd -d /dev/nbd0