Add a new output type, tar-disk.

This option will create an optionally compressed tarball containing a
disk image. This format is used by Google's Compute Engine.

This also adds a new option, tar_disk_name, to set the name of the disk
image that will be wrapped in the final tarball. opts.image_name
continues to be the final output file name.
This commit is contained in:
David Shea 2019-03-28 15:53:14 -04:00
parent c585b91422
commit c941b82b0c
5 changed files with 125 additions and 12 deletions

View File

@ -550,9 +550,13 @@ def start_build(cfg, dnflock, gitlock, branch, recipe_name, compose_type, test_m
cfg_args["volid"] = "" cfg_args["volid"] = ""
cfg_args["extra_boot_args"] = get_kernel_append(recipe) cfg_args["extra_boot_args"] = get_kernel_append(recipe)
if "compression" not in cfg_args:
cfg_args["compression"] = "xz"
if "compress_args" not in cfg_args:
cfg_args["compress_args"] = []
cfg_args.update({ cfg_args.update({
"compression": "xz",
"compress_args": [],
"ks": [ks_path], "ks": [ks_path],
"logfile": log_dir, "logfile": log_dir,
"timeout": 60, # 60 minute timeout "timeout": 60, # 60 minute timeout
@ -597,6 +601,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": True, "make_tar": True,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -608,6 +613,7 @@ def compose_args(compose_type):
"image_type": False, # False instead of None because of TOML "image_type": False, # False instead of None because of TOML
"qemu_args": [], "qemu_args": [],
"image_name": default_image_name("xz", "root.tar"), "image_name": default_image_name("xz", "root.tar"),
"tar_disk_name": None,
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
"app_template": None, "app_template": None,
@ -619,6 +625,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -630,6 +637,7 @@ def compose_args(compose_type):
"image_type": False, # False instead of None because of TOML "image_type": False, # False instead of None because of TOML
"qemu_args": [], "qemu_args": [],
"image_name": "live.iso", "image_name": "live.iso",
"tar_disk_name": None,
"fs_label": "Anaconda", # Live booting may expect this to be 'Anaconda' "fs_label": "Anaconda", # Live booting may expect this to be 'Anaconda'
"image_only": False, "image_only": False,
"app_name": None, "app_name": None,
@ -644,6 +652,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -655,6 +664,7 @@ def compose_args(compose_type):
"image_type": False, # False instead of None because of TOML "image_type": False, # False instead of None because of TOML
"qemu_args": [], "qemu_args": [],
"image_name": "disk.img", "image_name": "disk.img",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -667,6 +677,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -678,6 +689,7 @@ def compose_args(compose_type):
"image_type": "qcow2", "image_type": "qcow2",
"qemu_args": [], "qemu_args": [],
"image_name": "disk.qcow2", "image_name": "disk.qcow2",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -690,6 +702,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -701,6 +714,7 @@ def compose_args(compose_type):
"image_type": False, # False instead of None because of TOML "image_type": False, # False instead of None because of TOML
"qemu_args": [], "qemu_args": [],
"image_name": "filesystem.img", "image_name": "filesystem.img",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -713,6 +727,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -724,6 +739,7 @@ def compose_args(compose_type):
"image_type": False, "image_type": False,
"qemu_args": [], "qemu_args": [],
"image_name": "disk.ami", "image_name": "disk.ami",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -736,6 +752,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -747,6 +764,7 @@ def compose_args(compose_type):
"image_type": "vpc", "image_type": "vpc",
"qemu_args": ["-o", "subformat=fixed,force_size"], "qemu_args": ["-o", "subformat=fixed,force_size"],
"image_name": "disk.vhd", "image_name": "disk.vhd",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -759,6 +777,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -770,6 +789,7 @@ def compose_args(compose_type):
"image_type": "vmdk", "image_type": "vmdk",
"qemu_args": [], "qemu_args": [],
"image_name": "disk.vmdk", "image_name": "disk.vmdk",
"tar_disk_name": None,
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,
@ -782,6 +802,7 @@ def compose_args(compose_type):
"make_appliance": False, "make_appliance": False,
"make_ami": False, "make_ami": False,
"make_tar": False, "make_tar": False,
"make_tar_disk": False,
"make_pxe_live": False, "make_pxe_live": False,
"make_ostree_live": False, "make_ostree_live": False,
"make_oci": False, "make_oci": False,
@ -793,6 +814,34 @@ def compose_args(compose_type):
"image_type": "qcow2", "image_type": "qcow2",
"qemu_args": [], "qemu_args": [],
"image_name": "disk.qcow2", "image_name": "disk.qcow2",
"tar_disk_name": None,
"fs_label": "",
"image_only": True,
"app_name": None,
"app_template": None,
"app_file": None,
},
"google": {"make_iso": False,
"make_disk": True,
"make_fsimage": False,
"make_appliance": False,
"make_ami": False,
"make_tar": False,
"make_tar_disk": True,
"make_pxe_live": False,
"make_ostree_live": False,
"make_oci": False,
"make_vagrant": False,
"ostree": False,
"live_rootfs_keep_size": False,
"live_rootfs_size": 0,
"image_size_align": 1024,
"image_type": False, # False instead of None because of TOML
"qemu_args": [],
"image_name": "disk.tar.gz",
"tar_disk_name": "disk.raw",
"compression": "gzip",
"compress_args": ["-9"],
"fs_label": "", "fs_label": "",
"image_only": True, "image_only": True,
"app_name": None, "app_name": None,

View File

@ -148,6 +148,8 @@ def lmc_parser(dracut_default=""):
help="Build an ami image") help="Build an ami image")
action.add_argument("--make-tar", action="store_true", action.add_argument("--make-tar", action="store_true",
help="Build a tar of the root filesystem") help="Build a tar of the root filesystem")
action.add_argument("--make-tar-disk", action="store_true",
help="Build a tar of a partitioned disk image")
action.add_argument("--make-pxe-live", action="store_true", action.add_argument("--make-pxe-live", action="store_true",
help="Build a live pxe boot squashfs image") help="Build a live pxe boot squashfs image")
action.add_argument("--make-ostree-live", action="store_true", action.add_argument("--make-ostree-live", action="store_true",
@ -215,6 +217,8 @@ def lmc_parser(dracut_default=""):
help="Path to existing filesystem image to use for creating final image.") help="Path to existing filesystem image to use for creating final image.")
image_group.add_argument("--image-name", default=None, image_group.add_argument("--image-name", default=None,
help="Name of output file to create. Used for tar, fs and disk image. Default is a random name.") help="Name of output file to create. Used for tar, fs and disk image. Default is a random name.")
image_group.add_argument("--tar-disk-name", default=None,
help="Name of the archive member for make-tar-disk.")
image_group.add_argument("--fs-label", default="Anaconda", image_group.add_argument("--fs-label", default="Anaconda",
help="Label to set on fsimage, default is 'Anaconda'") help="Label to set on fsimage, default is 'Anaconda'")
image_group.add_argument("--image-size-align", type=int, default=0, image_group.add_argument("--image-size-align", type=int, default=0,

View File

@ -487,28 +487,49 @@ def make_image(opts, ks, cancel_func=None):
Use qemu+boot.iso or anaconda to install to a disk image. Use qemu+boot.iso or anaconda to install to a disk image.
""" """
if opts.image_name:
# For make_tar_disk, opts.image_name is the name of the final tarball.
# Use opts.tar_disk_name as the name of the disk image
if opts.make_tar_disk:
disk_img = joinpaths(opts.result_dir, opts.tar_disk_name)
elif opts.image_name:
disk_img = joinpaths(opts.result_dir, opts.image_name) disk_img = joinpaths(opts.result_dir, opts.image_name)
else: else:
disk_img = tempfile.mktemp(prefix="lmc-disk-", suffix=".img", dir=opts.result_dir) disk_img = tempfile.mktemp(prefix="lmc-disk-", suffix=".img", dir=opts.result_dir)
log.info("disk_img = %s", disk_img) log.info("disk_img = %s", disk_img)
disk_size = calculate_disk_size(opts, ks) disk_size = calculate_disk_size(opts, ks)
# For make_tar_disk, pass a second path parameter for the final tarball
# not the final output file.
if opts.make_tar_disk:
tar_img = joinpaths(opts.result_dir, opts.image_name)
else:
tar_img = None
try: try:
if opts.no_virt: if opts.no_virt:
novirt_install(opts, disk_img, disk_size, cancel_func=cancel_func) novirt_install(opts, disk_img, disk_size, cancel_func=cancel_func, tar_img=tar_img)
else: else:
install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log" install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
log.info("install_log = %s", install_log) log.info("install_log = %s", install_log)
virt_install(opts, install_log, disk_img, disk_size, cancel_func=cancel_func) virt_install(opts, install_log, disk_img, disk_size, cancel_func=cancel_func, tar_img=tar_img)
except InstallError as e: except InstallError as e:
log.error("Install failed: %s", e) log.error("Install failed: %s", e)
if not opts.keep_image and os.path.exists(disk_img): if not opts.keep_image:
if os.path.exists(disk_img):
log.info("Removing bad disk image") log.info("Removing bad disk image")
os.unlink(disk_img) os.unlink(disk_img)
if tar_img and os.path.exists(tar_img):
log.info("Removing bad tar file")
os.unlink(tar_img)
raise raise
log.info("Disk Image install successful") log.info("Disk Image install successful")
if opts.make_tar_disk:
return tar_img
return disk_img return disk_img

View File

@ -323,7 +323,7 @@ def anaconda_cleanup(dirinstall_path):
return rc return rc
def novirt_install(opts, disk_img, disk_size, cancel_func=None): def novirt_install(opts, disk_img, disk_size, cancel_func=None, tar_img=None):
""" """
Use Anaconda to install to a disk image Use Anaconda to install to a disk image
@ -333,6 +333,7 @@ def novirt_install(opts, disk_img, disk_size, cancel_func=None):
:param int disk_size: The size of the disk_img in MiB :param int disk_size: The size of the disk_img in MiB
:param cancel_func: Function that returns True to cancel build :param cancel_func: Function that returns True to cancel build
:type cancel_func: function :type cancel_func: function
:param str tar_img: For make_tar_disk, the path to final tarball to be created
This method runs anaconda to create the image and then based on the opts This method runs anaconda to create the image and then based on the opts
passed creates a qemu disk image or tarfile. passed creates a qemu disk image or tarfile.
@ -511,8 +512,20 @@ def novirt_install(opts, disk_img, disk_size, cancel_func=None):
# For raw disk images, use fallocate to deallocate unused space # For raw disk images, use fallocate to deallocate unused space
execWithRedirect("fallocate", ["--dig-holes", disk_img], raise_err=True) execWithRedirect("fallocate", ["--dig-holes", disk_img], raise_err=True)
# For make_tar_disk, wrap the result in a tar file, and remove the original disk image.
if opts.make_tar_disk:
compress_args = []
for arg in opts.compress_args:
compress_args += arg.split(" ", 1)
def virt_install(opts, install_log, disk_img, disk_size, cancel_func=None): rc = mktar(disk_img, tar_img, opts.compression, compress_args, selinux=False)
if rc:
raise InstallError("novirt_install mktar failed: rc=%s" % rc)
os.unlink(disk_img)
def virt_install(opts, install_log, disk_img, disk_size, cancel_func=None, tar_img=None):
""" """
Use qemu to install to a disk image Use qemu to install to a disk image
@ -523,6 +536,7 @@ def virt_install(opts, install_log, disk_img, disk_size, cancel_func=None):
:param int disk_size: The size of the disk_img in MiB :param int disk_size: The size of the disk_img in MiB
:param cancel_func: Function that returns True to cancel build :param cancel_func: Function that returns True to cancel build
:type cancel_func: function :type cancel_func: function
:param str tar_img: For make_tar_disk, the path to final tarball to be created
This uses qemu with a boot.iso and a kickstart to create a disk This uses qemu with a boot.iso and a kickstart to create a disk
image and then optionally, based on the opts passed, creates tarfile. image and then optionally, based on the opts passed, creates tarfile.
@ -637,3 +651,16 @@ def virt_install(opts, install_log, disk_img, disk_size, cancel_func=None):
if rc: if rc:
raise InstallError("virt_install failed") raise InstallError("virt_install failed")
shutil.rmtree(vagrant_dir) shutil.rmtree(vagrant_dir)
# For make_tar_disk, wrap the result in a tar file, and remove the original disk image.
if opts.make_tar_disk:
compress_args = []
for arg in opts.compress_args:
compress_args += arg.split(" ", 1)
rc = mktar(disk_img, tar_img, opts.compression, compress_args, selinux=False)
if rc:
raise InstallError("virt_install mktar failed: rc=%s" % rc)
os.unlink(disk_img)

View File

@ -99,6 +99,10 @@ def main():
errors.append("The appliance template (%s) doesn't " errors.append("The appliance template (%s) doesn't "
"exist" % opts.app_template) "exist" % opts.app_template)
if opts.make_tar_disk:
if opts.tar_disk_name and os.path.exists(joinpaths(opts.result_dir, opts.tar_disk_name)):
errors.append("The disk image to be created should not exist.")
else:
if opts.image_name and os.path.exists(joinpaths(opts.result_dir, opts.image_name)): if opts.image_name and os.path.exists(joinpaths(opts.result_dir, opts.image_name)):
errors.append("The disk image to be created should not exist.") errors.append("The disk image to be created should not exist.")
@ -173,6 +177,14 @@ def main():
opts.image_name = default_image_name(opts.compression, "vagrant.tar") opts.image_name = default_image_name(opts.compression, "vagrant.tar")
if opts.compression == "xz" and not opts.compress_args: if opts.compression == "xz" and not opts.compress_args:
opts.compress_args = ["-9"] opts.compress_args = ["-9"]
elif opts.make_tar_disk:
opts.make_disk = True
if not opts.image_name:
opts.image_name = "root.img"
if not opts.tar_disk_name:
opts.tar_disk_name = default_image_name(opts.compression, "root.tar")
if opts.compression == "xz" and not opts.compress_args:
opts.compress_args = ["-9"]
if opts.app_file: if opts.app_file:
opts.app_file = joinpaths(opts.result_dir, opts.app_file) opts.app_file = joinpaths(opts.result_dir, opts.app_file)