Skip to content

Instantly share code, notes, and snippets.

@andreztz
Forked from sandeep-datta/create_img
Created November 16, 2024 16:41
Show Gist options
  • Select an option

  • Save andreztz/44d4c9656193a914dc8e45c2a6a3fd73 to your computer and use it in GitHub Desktop.

Select an option

Save andreztz/44d4c9656193a914dc8e45c2a6a3fd73 to your computer and use it in GitHub Desktop.
A python script for creating a linux virtual disk with a ready to use rootfs
#!/usr/bin/python3
import sys
import os
import argparse
from os import path
from pprint import pprint
from tempfile import TemporaryDirectory
from sh import dd, sudo, ls, losetup, parted, kpartx, mke2fs, grub_install, mount, umount, cp, chmod, ln, chown
def showArgs(args):
for i,arg in enumerate(args):
print("{}) {}".format(i, arg))
def ensureNewFileIsCreated(fileName, func):
if path.isfile(fileName):
os.remove(fileName)
func()
if not path.isfile(fileName):
raise Exception("Error: could not create raw image file:{}".format(fileName))
def createRawImageFile(fileName, imageSize):
def func():
dd("if=/dev/zero", "of={}".format(fileName), "bs=1", "count=0", "seek={}".format(imageSize))
ensureNewFileIsCreated(fileName, func)
def main():
try:
print("-------CREATE LINUX VIRTUAL DISK WITH ROOTFS--------")
parser = argparse.ArgumentParser()
parser.add_argument("imageFileName", help="Path to the output image file")
parser.add_argument("imageFileSize", help="Size of the output image file. You can use suffixes allowed by the dd command.")
parser.add_argument("--kernel", help="Path to a kernel image. This file will be copied to /boot on the primary partition of the disk image")
parser.add_argument("--busybox", help="Path to the busybox executable. This file will be copied to /bin on the primary partition of the disk image. Links to several useful commands will also be created.")
parser.add_argument("--grubcfg", help="Path to a grub.cfg file. A customized version of grub.cfg can be used to boot linux directly and avoid entering commands on the grub prompt.")
args = parser.parse_args()
createRawImageFile(args.imageFileName, args.imageFileSize)
chown('sandeepd:sandeepd', args.imageFileName)
loop0 = losetup('-f', '--show', args.imageFileName).strip()
print("Disk image file looped at:{}".format(loop0))
try:
print("Creating msdos partition table")
parted(loop0, 'mklabel msdos')
print("Creating primary partition")
parted(loop0, 'mkpart primary 1 100%')
try:
print("Looping primary partition to /dev/mapper")
kpartx('-va', loop0)
try:
loop1 = losetup('-f','--show', "/dev/mapper/{}p1".format(path.basename(loop0))).strip()
print("Primary partition looped at:{}".format(loop1))
print("Formatting primary partition")
mke2fs(loop1)
with TemporaryDirectory(dir='/mnt') as dirName:
print("Created temporary mount point for primary partion")
print("Mounting partion 1 at:", dirName)
mount(loop1, dirName)
bootDir = path.join(dirName, 'boot')
print("Installing GRUB2")
grub_install('--boot-directory={}'.format(bootDir), '--modules=ext2 part_msdos', loop0)
print(ls('-lh', bootDir))
if args.kernel:
print("Copying kernel to /boot on primary partition")
cp(args.kernel, bootDir)
print(ls('-lh', bootDir))
if args.busybox:
print("Installing busybox to /bin")
binDir = path.join(dirName, 'bin')
os.makedirs(binDir)
busboxTargetPath = path.join(binDir,'busybox')
cp(args.busybox, busboxTargetPath)
chmod('+x', busboxTargetPath)
cwd=os.getcwd()
os.chdir(binDir)
for cmd in ['ash', 'sh', 'ls', 'rm', 'mkdir', 'rmdir', 'grep', 'find', 'mount', 'umount']:
#Use relative paths here.
ln('-s', 'busybox', cmd)
os.chdir(cwd)
print(ls('-lh', binDir))
if args.grubcfg:
print("Copying '{}' to /boot/grub".format(args.grubcfg))
cp(args.grubcfg, path.join(bootDir, 'grub'))
umount(loop1)
except Exception as e:
print(e)
finally:
losetup('-d', loop1)
except Exception as e:
print(e)
finally:
kpartx('-vd', loop0)
except Exception as e:
print(e)
finally:
losetup('-d', loop0)
except Exception as e:
print(e)
if __name__ == "__main__":
main()
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#
### BEGIN /etc/grub.d/00_header ###
if [ -s $prefix/grubenv ]; then
set have_grubenv=true
load_env
fi
set default="0"
if [ x"${feature_menuentry_id}" = xy ]; then
menuentry_id_option="--id"
else
menuentry_id_option=""
fi
export menuentry_id_option
if [ "${prev_saved_entry}" ]; then
set saved_entry="${prev_saved_entry}"
save_env saved_entry
set prev_saved_entry=
save_env prev_saved_entry
set boot_once=true
fi
function savedefault {
if [ -z "${boot_once}" ]; then
saved_entry="${chosen}"
save_env saved_entry
fi
}
function recordfail {
set recordfail=1
if [ -n "${have_grubenv}" ]; then if [ -z "${boot_once}" ]; then save_env recordfail; fi; fi
}
function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}
if [ x$feature_default_font_path = xy ] ; then
font=unicode
else
insmod part_msdos
insmod ext2
set root='hd1,msdos5'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint-bios=hd1,msdos5 --hint-efi=hd1,msdos5 --hint-baremetal=ahci1,msdos5 cd80328b-d252-488d-b9e2-ffa808ce5d83
else
search --no-floppy --fs-uuid --set=root cd80328b-d252-488d-b9e2-ffa808ce5d83
fi
font="/usr/share/grub/unicode.pf2"
fi
if loadfont $font ; then
set gfxmode=auto
load_video
insmod gfxterm
set locale_dir=$prefix/locale
set lang=en_IN
insmod gettext
fi
terminal_output gfxterm
if [ "${recordfail}" = 1 ]; then
set timeout=-1
else
set timeout=10
fi
### END /etc/grub.d/00_header ###
### BEGIN /etc/grub.d/05_debian_theme ###
set menu_color_normal=white/black
set menu_color_highlight=black/light-gray
if background_color 44,0,30; then
clear
fi
### END /etc/grub.d/05_debian_theme ###
### BEGIN /etc/grub.d/10_linux ###
function gfxmode {
set gfxpayload="${1}"
if [ "${1}" = "keep" ]; then
set vt_handoff=vt.handoff=7
else
set vt_handoff=
fi
}
if [ "${recordfail}" != 1 ]; then
if [ -e ${prefix}/gfxblacklist.txt ]; then
if hwmatch ${prefix}/gfxblacklist.txt 3; then
if [ ${match} = 0 ]; then
set linux_gfx_mode=keep
else
set linux_gfx_mode=text
fi
else
set linux_gfx_mode=text
fi
else
set linux_gfx_mode=keep
fi
else
set linux_gfx_mode=text
fi
export linux_gfx_mode
if [ "${linux_gfx_mode}" != "text" ]; then load_video; fi
menuentry 'Linux baremetal' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-cd80328b-d252-488d-b9e2-ffa808ce5d83' {
recordfail
gfxmode $linux_gfx_mode
insmod gzio
insmod part_msdos
insmod ext2
set root='hd0,msdos1'
linux /boot/bzImage root=/dev/sda1 init=/bin/sh rw
#initrd /boot/initrd.img-3.5.0-43-generic
}
### END /etc/grub.d/10_linux ###
### BEGIN /etc/grub.d/30_os-prober ###
if [ "x${timeout}" != "x-1" ]; then
if keystatus; then
if keystatus --shift; then
set timeout=-1
else
set timeout=0
fi
else
if sleep --interruptible 3 ; then
set timeout=0
fi
fi
fi
### END /etc/grub.d/30_os-prober ###
### BEGIN /etc/grub.d/30_uefi-firmware ###
### END /etc/grub.d/30_uefi-firmware ###
### BEGIN /etc/grub.d/40_custom ###
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
### END /etc/grub.d/40_custom ###
### BEGIN /etc/grub.d/41_custom ###
if [ -f ${config_directory}/custom.cfg ]; then
source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then
source $prefix/custom.cfg;
fi
### END /etc/grub.d/41_custom ###
#!/usr/bin/python3
import sys
import os
import argparse
from os import path
from pprint import pprint
from tempfile import TemporaryDirectory
from sh import dd, sudo, ls, losetup, parted, kpartx, mke2fs, grub_install, mount, umount, cp, chmod, ln
def main():
try:
print("-------MOUNT VIRTUAL DISK CREATED WITH CREATE_IMG--------")
parser = argparse.ArgumentParser()
parser.add_argument("imageFileName", help="Path to the image file")
parser.add_argument("mountPoint", help="The mount point for the primary partition")
#parser.add_argument("-d", help="Delete all existing loopbacks and mounts")
args = parser.parse_args()
loop0 = losetup('-f', '--show', args.imageFileName).strip()
print("Disk image file looped at:{}".format(loop0))
print("Looping primary partition to /dev/mapper")
kpartx('-va', loop0)
loop1 = losetup('-f','--show', "/dev/mapper/{}p1".format(path.basename(loop0))).strip()
print("Primary partition looped at:{}".format(loop1))
print("Created temporary mount point for primary partion")
print("Mounting partion 1 at:", args.mountPoint)
mount(loop1, args.mountPoint)
with open("unmount_img", "w") as unmountScript:
print("#!/bin/bash", file=unmountScript)
print("umount {}".format(loop1), file=unmountScript)
print("losetup -d {}".format(loop1), file=unmountScript)
print("kpartx -vd {}".format(loop0), file=unmountScript)
print("losetup -d {}".format(loop0), file=unmountScript)
chmod('+x', "unmount_img")
except Exception as e:
print(e)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment