Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 241 lines (198 sloc) 6.85 KB
#!/bin/bash -e
# depends: qemu grub2 parted kpartx zip ovftool
# Copyright (c) 2011-2015 TurnKey GNU/Linux -
# This file is part of buildtasks.
# Buildtasks is free software; you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by the
# Free Software Foundation; either version 3 of the License, or (at your
# option) any later version.
fatal() { echo "FATAL [$(basename $0)]: $@" 1>&2; exit 1; }
info() { echo "INFO [$(basename $0)]: $@"; }
usage() {
Syntax: $0 rootfs
Bundles rootfs into VM optimized builds (vmdk and ovf)
rootfs - root filesystem path
VM_URL - Product URL ($VM_URL)
VM_MEMORY - Amount of memory - integer ($VM_MEMORY)
VM_FULLNAME - Display name of image ($VM_FULLNAME)
VM_GUESTOS - Guest OS of image ($VM_GUESTOS)
exit 1
if [[ "$#" != "1" ]]; then
name=$(basename $rootfs .rootfs)
parsed_appname_version=$($BT/bin/parse-appname-version ${name#turnkey-})
read appname appversion codename arch <<< "$parsed_appname_version"
[ -n "$VM_URL" ] || fatal "VM_URL not set"
[ -n "$VM_MEMORY" ] || fatal "VM_MEMORY not set"
[ -n "$VM_FULLNAME" ] || fatal "VM_FULLNAME not set"
[ -n "$VM_GUESTOS" ] || fatal "VM_GUESTOS not set"
# image size consts.
VM_PARTED_LVM="63 20000" # 20GB (free: ~2.5GB)
[ -e /dev/$VG ] && VG=turnkeyvm
[ -e /dev/$VG ] && fatal "VG exists: $VG"
info "using volume group: $VG"
info "creating raw image ($VM_SIZE sparse) and partitions"
qemu-img create -f raw $rootfs.raw $VM_SIZE
parted --script $rootfs.raw mklabel msdos
parted --script -- $rootfs.raw mkpart primary $VM_PARTED_LVM
parted --script -- $rootfs.raw set 1 boot on
parted --script -- $rootfs.raw set 1 LVM on
info "setting up loop device and checking that it's mounted"
loopdev=$(kpartx -avs $rootfs.raw | cut -d' ' -f3)
for i in {1..11}; do
if ls /dev/mapper/$loopdev >/dev/null 2>&1; then
info "success - $loopdev is mounted (waited $(( i - 1)) seconds(s))"
elif [ "$i" -eq 11 ]; then
fatal "$loopdev is NOT mounted (waited 10 seconds)"
sleep 1
info "setting up LVM"
pvcreate /dev/mapper/$loopdev
vgcreate $VG /dev/mapper/$loopdev
lvcreate -L $VM_PARTED_LVM_ROOT -n root $VG
lvcreate -L $VM_PARTED_LVM_SWAP -n swap_1 $VG
vgchange -a y
info "creating filesystems (root & swap)"
mkfs.ext4 /dev/mapper/$VG-root
mkswap /dev/mapper/$VG-swap_1
info "mounting raw root partition and syncing rootfs"
mkdir $rootfs.vm
mount /dev/mapper/$VG-root $rootfs.vm
rsync -a -t -r -S -I -H $rootfs/ $rootfs.vm
info "mount binding dev and proc"
mount --rbind --make-rslave /dev $rootfs.vm/dev
mount --rbind --make-rslave /sys $rootfs.vm/sys
mount --rbind --make-rslave /proc $rootfs.vm/proc
info "installing and configuring grub"
cat > $devicemap <<EOF
(hd0) /dev/${loopdev%p1}
chroot $rootfs.vm grub-mkconfig -o /boot/grub/grub.cfg
grub-install --force --modules='part_msdos ext2 search_fs_uuid' --root-directory=$rootfs.vm /dev/${loopdev%p1}
rm -f $devicemap
info "creating fstab"
echo -e "/dev/mapper/$VG-root\t/\text4\terrors=remount-ro\t0\t1" > $fstab
echo -e "/dev/mapper/$VG-swap_1\tnone\tswap\tsw\t0\t0" >> $fstab
info "umounting the jumble..."
sync # ensure everything is written from cache before trying to umount
umount $rootfs.vm/proc
grep $rootfs.vm/sys /proc/mounts | cut -f2 -d" " | sort -r | xargs umount -n
umount $rootfs.vm/dev
umount $rootfs.vm/
info "cleaning up volume groups and mounts"
# udev seems to be holding files open on occasion?!
systemctl restart systemd-udevd.service
vgchange -a n $VG
kpartx -dv $rootfs.raw
dmsetup remove_all
rmdir $rootfs.vm
rm -f /etc/lvm/backup/$VG
info "converting raw image to vmdk"
qemu-img convert -f raw $rootfs.raw -O vmdk -o compat6 $name.vmdk
if [ -z "$BT_DEBUG" ]; then
info "cleaning up $rootfs.raw as it's no longer needed"
rm -f $rootfs.raw
info "creating vmx"
cat > $name.vmx<<EOF
config.version = "8"
virtualHW.version = "7"
memsize = "$VM_MEMORY"
Ethernet0.present = "TRUE"
Ethernet0.connectionType = "bridged"
displayName = "$VM_FULLNAME"
annotation = "$VM_URL"
guestinfo.vmware.product.long = "$VM_FULLNAME"
guestinfo.vmware.product.url = "$VM_URL"
guestinfo.vmware.product.class = "virtual machine"
guestOS = "$VM_GUESTOS"
priority.grabbed = "normal"
priority.ungrabbed = "normal"
usb.present = "TRUE"
tools.syncTime = "TRUE"
usb.generic.autoconnect = "TRUE"
powerType.powerOff = "hard"
powerType.powerOn = "hard"
powerType.suspend = "hard"
powerType.reset = "hard"
floppy0.present = "FALSE"
scsi0.present = "TRUE"
scsi0.virtualDev = "lsilogic"
scsi0:0.deviceType = "disk"
scsi0:0.present = "TRUE"
scsi0:0.fileName = "$name.vmdk"
scsi0:0.mode = "persistent"
scsi0:0.startConnected = "TRUE"
scsi0:0.writeThrough = "TRUE"
ide1:0.present = "TRUE"
ide1:0.deviceType = "cdrom-raw"
ide1:0.startConnected = "TRUE"
ide1:0.fileName = "auto detect"
ide1:0.autodetect = "TRUE"
info "setting up image directory"
mkdir $name
mv $name.vmx $name.vmdk $name/
cat > $name/README.txt <<EOF
For virtualization specific documentation, please refer to:
chown -R root:root $name
chmod 600 $name/*
chmod 644 $name/$name.vmx
info "generating vmdk zip"
zip -r -T $ $name
info "generating ovf"
cd $name
ovftool $name.vmx $name.ovf
rm $name.vmx $name.vmdk
info "tweaking ovf & generating ova"
mv $name.ovf $name.ovf.tmp
# fix vm name (otherwise defaults to "vm" in VBox)
sed -i "/VirtualSystem ovf:id/ s|=\".*|=\"$VM_FULLNAME\">|" $name.ovf.tmp
# add additional product info - displays in VirtualBox on import
# and should also show in the UI of higher end VMware products
cp $BT/templates/ovf-productinfo productinfo.tmp
sed -i "s|@@APP@@|$appname|; \
s|@@VERSION@@|$appversion|; \
s|@@FULL_VERSION@@|$name|; \
s|@@URL@@|$VM_URL| \
" productinfo.tmp
sed -e "/<\/Name>/r./productinfo.tmp" $name.ovf.tmp > $name.ovf
# for widest compatibility, unfortunately we need to strip OS info and
# explicitly set OS to "Other 64-Bit". To be reviewed next major release.
sed -i '/OperatingSystemSection/ s|ovf:id="96.*|ovf:id="102">|' $name.ovf
# remove invalid ovf manifest
# (SHA sums won't match due to tweaked OVF; ovftool auto generates a new
# manifest when creating an OVA)
rm $
# SHA256 is default for ovftool v4.2, but older versions of vSphere/ESX only
# support SHA1. It's not a security issue as we hash the ova itself with
# SHA256 and then sign the hash file. To be reviewed next major release.
ovftool --shaAlgorithm=SHA1 $name.ovf ../$name.ova
cd ../
if [ -z "$BT_DEBUG" ]; then
info "cleaning up vm temp files"
rm -rf $name
You can’t perform that action at this time.