Saturday, April 4, 2015

Prepare a Custom Fedora VM from Commandline (No Installation) (Boot Virtual Disk with Grub)

So, today I wanted to create a Fedora VM, prepared beforehand, without any ISO, redistributable.
So, here is how I was able to achieve this:
This method was tested on a host machine running CentOS 6.6 as well as a host machine running Fedora 21.
First, create the virtual disk.
/t/fedora21 ❯❯❯ truncate -s 10G fedora21.img
Yeah, I know, one can use "qemu-img create -f raw fedora21.img 10G" too.
Find a free loop device and map the image to it automatically:
/t/fedora21 ❯❯❯ kpartx -va fedora21.img
Find out which loopback interface was our image mapped to:
/t/fedora21 ❯❯❯ losetup -a
/dev/loop0: [0802]:393335 (fedora21.img)
Write a DOS partition table:
/t/fedora21 ❯❯❯ fdisk /dev/loop0 
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0xf6e951e8.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 22: Invalid argument.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
Let us create a partition and mark it bootable:
/t/fedora21 ❯❯❯ fdisk /dev/loop0 

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1305, default 1): 
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-1305, default 1305): 
Using default value 1305

Command (m for help): a
Partition number (1-4): 1

Command (m for help): p

Disk /dev/loop0: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xf6e951e8

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1   *           1        1305    10482381   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 22: Invalid argument.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
Run kpartx again, to map the newly created partition as another loop device
/t/fedora21 ❯❯❯ kpartx -va fedora21.img
add map loop0p1 (253:0): 0 20964762 linear /dev/loop0 63
Format the newly created partition to ext4 filesystem.
/t/fedora21 ❯❯❯ mkfs.ext4 /dev/mapper/loop0p1
mke2fs 1.41.12 (17-May-2010)
Discarding device blocks: done                            
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2620595 blocks
131029 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 36 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
Mount the new partition to /mnt/fedora21/
/t/fedora21 ❯❯❯ mkdir -p /mnt/fedora21/; mount /dev/mapper/loop0p1 /mnt/fedora21/
Install mock (a simple program that helps in building source RPMs inside a chroot)
/t/fedora21 ❯❯❯ yum install -y mock
Initialize a directory structure for mock fedora21
/t/fedora21 ❯❯❯ sudo mock --init -v -r fedora-21-x86_64 --install grubby kernel grub2 passwd
/t/fedora21 ❯❯❯ #For configuring stuff, edit the file /etc/mock/fedora-21-x86_64.cfg or create a copy of it and play with it.
Copy all mock files to your disk image:
/t/fedora21 ❯❯❯ cp -r /var/lib/mock/fedora-21-x86_64/root/* /mnt/fedora21/
Note the UUID of the new partition
/t/fedora21 ❯❯❯ blkid /dev/mapper/loop0p1
/dev/mapper/loop0p1: UUID="9d4d0fec-3cdc-4d72-9f46-3aca30d981c5" TYPE="ext4"
Setup /etc/fstab
/t/fedora21 ❯❯❯ tee /mnt/fedora21/etc/fstab <<EOF
UUID="9d4d0fec-3cdc-4d72-9f46-3aca30d981c5" TYPE="ext4" /                       ext4    defaults        1 1
EOF
Mount and bind some important directories
for i in /dev /dev/pts /proc /sys; do mount -B $i /mnt/fedora21/$i ; done
Create a configuration file for grub
/t/fedora21 ❯❯❯ chroot /mnt/fedora21 /bin/bash -c "grub2-mkconfig -o /boot/grub2/grub.cfg"
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.19.3-200.fc21.x86_64
Found initrd image: /boot/initramfs-3.19.3-200.fc21.x86_64.img
done
Install grub (assuming legacy system)
/t/fedora21 ❯❯❯ chroot /mnt/fedora21 /bin/bash -c "grub2-install /dev/loop0 --target=i386-pc"
Installing for i386-pc platform.
Installation finished. No error reported.
If you were running this on a system which was booted in UEFI mode, then replace efi stuff inside the grub configuration, since it is not relevant.
/t/fedora21 ❯❯❯ chroot /mnt/fedora21 /bin/bash -c "sed -i 's/initrdefi/initrd/g' /boot/grub2/grub.cfg"
/t/fedora21 ❯❯❯ chroot /mnt/fedora21 /bin/bash -c "sed -i 's/linuxefi/linux/g' /boot/grub2/grub.cfg"
Setup root password
/t/fedora21 ❯❯❯ chroot /mnt/fedora21 /bin/bash -c "passwd"
Changing password for user root.
New password: 
BAD PASSWORD: The password is a palindrome
Retype new password: 
passwd: all authentication tokens updated successfully.
Unmount the partition
/t/fedora21 ❯❯❯ for i in /dev/pts /dev /proc /sys; do umount /mnt/fedora21/$i ; done; umount /mnt/fedora21/
Delete device mapping
/t/fedora21 ❯❯❯ kpartx -vd fedora21.img
del devmap : loop0p1
loop deleted : /dev/loop0
Lets test this! Mr. Qemu, hit it!
/t/fedora21 ❯❯❯ /usr/libexec/qemu-kvm fedora216.img 
VNC server running on `::1:5900'
And... we have what we want:
/t/fedora21 ❯❯❯ vncviewer :5900

TigerVNC Viewer for X version 1.1.0 - built Oct 30 2014 12:44:50
Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)
See http://www.tigervnc.org for information on TigerVNC.