Add filesystem image install support
This adds support for installing to a filesystem image instead of a partitioned disk image. It requires Anaconda's --dirinstall support. Also re-organized the code to break it up into smaller methods. This speeds up iso creation in no-virt mode by removing the need to copy the filesystem from the partitioned disk image to the filesystem image that is used to make the squashfs image.
This commit is contained in:
parent
894d8b4738
commit
8e84ef10c1
@ -2,7 +2,9 @@
|
|||||||
INTRO
|
INTRO
|
||||||
-----
|
-----
|
||||||
livemedia-creator uses Anaconda, kickstart and Lorax to create bootable media
|
livemedia-creator uses Anaconda, kickstart and Lorax to create bootable media
|
||||||
such as live iso's that use the same install path as a normal system install.
|
that use the same install path as a normal system install. It can be used to
|
||||||
|
make live isos, bootable (partitioned) disk images and filesystem images for
|
||||||
|
use with virtualization.
|
||||||
|
|
||||||
The general idea is to use virt-install to install into a disk image and then
|
The general idea is to use virt-install to install into a disk image and then
|
||||||
use the disk image to create the bootable media.
|
use the disk image to create the bootable media.
|
||||||
@ -18,12 +20,12 @@ minimum you need:
|
|||||||
QUICKSTART
|
QUICKSTART
|
||||||
----------
|
----------
|
||||||
sudo livemedia-creator --make-iso \
|
sudo livemedia-creator --make-iso \
|
||||||
--iso=/extra/iso/Fedora-16-x86_64-netinst.iso --ks=./fedora-livemedia.ks
|
--iso=/extra/iso/Fedora-18-x86_64-netinst.iso --ks=./docs/fedora-livemedia.ks
|
||||||
|
|
||||||
If you are using the lorax git repo you can run it like so:
|
If you are using the lorax git repo you can run it like so:
|
||||||
|
|
||||||
sudo PATH=./src/sbin/:$PATH PYTHONPATH=./src/ ./src/sbin/livemedia-creator \
|
sudo PATH=./src/sbin/:$PATH PYTHONPATH=./src/ ./src/sbin/livemedia-creator \
|
||||||
--make-iso --iso=/extra/iso/Fedora-16-x86_64-netinst.iso \
|
--make-iso --iso=/extra/iso/Fedora-18-x86_64-netinst.iso \
|
||||||
--ks=./docs/fedora-livemedia.ks --lorax-templates=./share/
|
--ks=./docs/fedora-livemedia.ks --lorax-templates=./share/
|
||||||
|
|
||||||
If you want to watch the install you can pass '--vnc vnc' and use a vnc
|
If you want to watch the install you can pass '--vnc vnc' and use a vnc
|
||||||
@ -35,16 +37,17 @@ to monitor the logs for fatal errors, but may not catch everything.
|
|||||||
|
|
||||||
HOW IT WORKS
|
HOW IT WORKS
|
||||||
------------
|
------------
|
||||||
The --make-* switches define the final output.
|
There are 2 stages, the install stage which produces a disk or filesystem
|
||||||
|
image as its output, and the boot media creation which uses the image as
|
||||||
|
its input. Normally you would have it run both stages, but it is possible
|
||||||
|
to have it stop after the install stage, using --image-only, or to have it
|
||||||
|
skip the install stage and use a previously created disk image by passing
|
||||||
|
--disk-image or --fs-image
|
||||||
|
|
||||||
You then need to either pass --iso and --ks in order to create a disk image
|
When creating an iso virt-install boots using the passed Anaconda installer iso
|
||||||
using virt-install, or --disk-image to use a disk image from a previous run
|
and installs the system based on the kickstart. The %post section of the
|
||||||
to create the .iso
|
kickstart is used to customize the installed system in the same way that
|
||||||
|
current spin-kickstarts do.
|
||||||
virt-install boots using the passed Anaconda installer iso and installs the
|
|
||||||
system based on the kickstart. The %post section of the kickstart is used to
|
|
||||||
customize the installed system in the same way that current spin-kickstarts
|
|
||||||
do.
|
|
||||||
|
|
||||||
livemedia-creator monitors the install process for problems by watching the
|
livemedia-creator monitors the install process for problems by watching the
|
||||||
install logs. They are written to the current directory or to the base
|
install logs. They are written to the current directory or to the base
|
||||||
@ -129,7 +132,7 @@ it as well.
|
|||||||
ANACONDA IMAGE INSTALL
|
ANACONDA IMAGE INSTALL
|
||||||
----------------------
|
----------------------
|
||||||
You can create images without using virt-install by passing --no-virt on the
|
You can create images without using virt-install by passing --no-virt on the
|
||||||
cmdline. This will use Anaconda's image install feature to handle the install.
|
cmdline. This will use Anaconda's directory install feature to handle the install.
|
||||||
There are a couple of things to keep in mind when doing this:
|
There are a couple of things to keep in mind when doing this:
|
||||||
|
|
||||||
1. It will be most reliable when building images for the same release that the
|
1. It will be most reliable when building images for the same release that the
|
||||||
@ -168,10 +171,11 @@ This will produce an ami-root.img file in the working directory.
|
|||||||
At this time I have not tested the image with EC2. Feedback would we welcome.
|
At this time I have not tested the image with EC2. Feedback would we welcome.
|
||||||
|
|
||||||
|
|
||||||
APPLIANCE CREATION ------------------ livemedia-creator can now replace
|
APPLIANCE CREATION
|
||||||
appliance-tools by using the --make-appliance switch. This will create the
|
------------------
|
||||||
partitioned disk image and an XML file that can be used with virt-image to
|
livemedia-creator can now replace appliance-tools by using the --make-appliance
|
||||||
setup a virtual system.
|
switch. This will create the partitioned disk image and an XML file that can be
|
||||||
|
used with virt-image to setup a virtual system.
|
||||||
|
|
||||||
The XML is generated using the Mako template from
|
The XML is generated using the Mako template from
|
||||||
/usr/share/lorax/appliance/virt-image.xml You can use a different template by
|
/usr/share/lorax/appliance/virt-image.xml You can use a different template by
|
||||||
@ -218,7 +222,9 @@ disk image. Or you can pass --keep-image to keep it around after lorax is
|
|||||||
run.
|
run.
|
||||||
|
|
||||||
Cleaning up aborted --no-virt installs can sometimes be accomplished by running
|
Cleaning up aborted --no-virt installs can sometimes be accomplished by running
|
||||||
the anaconda-cleanup script.
|
the anaconda-cleanup script. As of f18 anaconda is multi-threaded and it can
|
||||||
|
sometimes become stuck and refuse to exit. When this happens you can usually
|
||||||
|
clean up by first killing the anaconda process then running anaconda-cleanup.
|
||||||
|
|
||||||
|
|
||||||
HACKING
|
HACKING
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Live Media Creator
|
# Live Media Creator
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2012 Red Hat, Inc.
|
# Copyright (C) 2011-2013 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -34,7 +34,6 @@ import threading
|
|||||||
import SocketServer
|
import SocketServer
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import shutil
|
import shutil
|
||||||
import traceback
|
|
||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
@ -49,9 +48,10 @@ from mako.exceptions import text_error_template
|
|||||||
# Use the Lorax treebuilder branch for iso creation
|
# Use the Lorax treebuilder branch for iso creation
|
||||||
from pylorax.base import DataHolder
|
from pylorax.base import DataHolder
|
||||||
from pylorax.treebuilder import TreeBuilder, RuntimeBuilder, udev_escape
|
from pylorax.treebuilder import TreeBuilder, RuntimeBuilder, udev_escape
|
||||||
from pylorax.sysutils import joinpaths, remove, linktree
|
from pylorax.sysutils import joinpaths, remove
|
||||||
from pylorax.imgutils import PartitionMount, mksparse, mkext4img, loop_detach
|
from pylorax.imgutils import PartitionMount, mksparse, mkext4img, loop_detach
|
||||||
from pylorax.imgutils import get_loop_name, dm_detach
|
from pylorax.imgutils import get_loop_name, dm_detach, mount, umount, Mount
|
||||||
|
from pylorax.imgutils import mksquashfs
|
||||||
from pylorax.executils import execWithRedirect, execWithCapture
|
from pylorax.executils import execWithRedirect, execWithCapture
|
||||||
|
|
||||||
# no-virt mode doesn't need libvirt, so make it optional
|
# no-virt mode doesn't need libvirt, so make it optional
|
||||||
@ -64,6 +64,12 @@ except ImportError:
|
|||||||
DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom",
|
DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom",
|
||||||
"--omit", "plymouth"]
|
"--omit", "plymouth"]
|
||||||
|
|
||||||
|
ROOT_PATH = "/mnt/sysimage/"
|
||||||
|
RUNTIME = "images/install.img"
|
||||||
|
|
||||||
|
class InstallError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LogRequestHandler(SocketServer.BaseRequestHandler):
|
class LogRequestHandler(SocketServer.BaseRequestHandler):
|
||||||
"""
|
"""
|
||||||
@ -184,11 +190,7 @@ class IsoMountpoint(object):
|
|||||||
self.initrd_path = initrd_path
|
self.initrd_path = initrd_path
|
||||||
|
|
||||||
if not self.initrd_path:
|
if not self.initrd_path:
|
||||||
self.mount_dir = tempfile.mkdtemp()
|
self.mount_dir = mount(self.iso_path, opts="loop")
|
||||||
if not self.mount_dir:
|
|
||||||
raise Exception("Error creating temporary directory")
|
|
||||||
|
|
||||||
execWithRedirect("mount", ["-o", "loop", self.iso_path, self.mount_dir])
|
|
||||||
else:
|
else:
|
||||||
self.mount_dir = self.initrd_path
|
self.mount_dir = self.initrd_path
|
||||||
|
|
||||||
@ -213,8 +215,7 @@ class IsoMountpoint(object):
|
|||||||
|
|
||||||
def umount( self ):
|
def umount( self ):
|
||||||
if not self.initrd_path:
|
if not self.initrd_path:
|
||||||
execWithRedirect("umount", [self.mount_dir])
|
umount(self.mount_dir)
|
||||||
os.rmdir( self.mount_dir )
|
|
||||||
|
|
||||||
def get_iso_label( self ):
|
def get_iso_label( self ):
|
||||||
"""
|
"""
|
||||||
@ -252,55 +253,55 @@ class VirtualInstall( object ):
|
|||||||
self.virt_name = "LiveOS-"+str(uuid.uuid4())
|
self.virt_name = "LiveOS-"+str(uuid.uuid4())
|
||||||
# add --graphics none later
|
# add --graphics none later
|
||||||
# add whatever serial cmds are needed later
|
# add whatever serial cmds are needed later
|
||||||
cmd = [ "virt-install", "-n", self.virt_name,
|
args = ["-n", self.virt_name,
|
||||||
"-r", str(memory),
|
"-r", str(memory),
|
||||||
"--noreboot",
|
"--noreboot",
|
||||||
"--noautoconsole" ]
|
"--noautoconsole"]
|
||||||
|
|
||||||
cmd.append("--graphics")
|
args.append("--graphics")
|
||||||
if vnc:
|
if vnc:
|
||||||
cmd.append(vnc)
|
args.append(vnc)
|
||||||
else:
|
else:
|
||||||
cmd.append("none")
|
args.append("none")
|
||||||
|
|
||||||
for ks in ks_paths:
|
for ks in ks_paths:
|
||||||
cmd.append("--initrd-inject")
|
args.append("--initrd-inject")
|
||||||
cmd.append(ks)
|
args.append(ks)
|
||||||
|
|
||||||
disk_opts = "path={0}".format(disk_img)
|
disk_opts = "path={0}".format(disk_img)
|
||||||
disk_opts += ",format=raw"
|
disk_opts += ",format=raw"
|
||||||
if not os.path.isfile(disk_img):
|
if not os.path.isfile(disk_img):
|
||||||
disk_opts += ",size={0}".format(img_size)
|
disk_opts += ",size={0}".format(img_size)
|
||||||
cmd.append("--disk")
|
args.append("--disk")
|
||||||
cmd.append(disk_opts)
|
args.append(disk_opts)
|
||||||
|
|
||||||
if iso.liveos:
|
if iso.liveos:
|
||||||
disk_opts = "path={0},device=cdrom".format(iso.iso_path)
|
disk_opts = "path={0},device=cdrom".format(iso.iso_path)
|
||||||
cmd.append("--disk")
|
args.append("--disk")
|
||||||
cmd.append(disk_opts)
|
args.append(disk_opts)
|
||||||
|
|
||||||
extra_args = "ks=file:/{0}".format(os.path.basename(ks_paths[0]))
|
extra_args = "ks=file:/{0}".format(os.path.basename(ks_paths[0]))
|
||||||
if kernel_args:
|
if kernel_args:
|
||||||
extra_args += " "+kernel_args
|
extra_args += " "+kernel_args
|
||||||
if iso.liveos:
|
if iso.liveos:
|
||||||
extra_args += " stage2=live:CDLABEL={0}".format(udev_escape(iso.label))
|
extra_args += " stage2=live:CDLABEL={0}".format(udev_escape(iso.label))
|
||||||
cmd.append("--extra-args")
|
args.append("--extra-args")
|
||||||
cmd.append(extra_args)
|
args.append(extra_args)
|
||||||
|
|
||||||
cmd.append("--location")
|
args.append("--location")
|
||||||
cmd.append(iso.mount_dir)
|
args.append(iso.mount_dir)
|
||||||
|
|
||||||
channel_args = "tcp,host={0}:{1},mode=connect,target_type=virtio" \
|
channel_args = "tcp,host={0}:{1},mode=connect,target_type=virtio" \
|
||||||
",name=org.fedoraproject.anaconda.log.0".format(
|
",name=org.fedoraproject.anaconda.log.0".format(
|
||||||
virtio_host, virtio_port)
|
virtio_host, virtio_port)
|
||||||
cmd.append("--channel")
|
args.append("--channel")
|
||||||
cmd.append(channel_args)
|
args.append(channel_args)
|
||||||
|
|
||||||
if arch:
|
if arch:
|
||||||
cmd.append("--arch")
|
args.append("--arch")
|
||||||
cmd.append(arch)
|
args.append(arch)
|
||||||
|
|
||||||
rc = execWithRedirect( cmd[0], cmd[1:] )
|
rc = execWithRedirect("virt-install", args)
|
||||||
if rc:
|
if rc:
|
||||||
raise Exception("Problem starting virtual install")
|
raise Exception("Problem starting virtual install")
|
||||||
|
|
||||||
@ -328,8 +329,8 @@ class VirtualInstall( object ):
|
|||||||
Could use libvirt for this instead.
|
Could use libvirt for this instead.
|
||||||
"""
|
"""
|
||||||
log.info( "Shutting down {0}".format(self.virt_name) )
|
log.info( "Shutting down {0}".format(self.virt_name) )
|
||||||
subprocess.call(["virsh","destroy",self.virt_name])
|
subprocess.call(["virsh", "destroy", self.virt_name])
|
||||||
subprocess.call(["virsh","undefine",self.virt_name])
|
subprocess.call(["virsh", "undefine", self.virt_name])
|
||||||
|
|
||||||
def is_image_mounted(disk_img):
|
def is_image_mounted(disk_img):
|
||||||
"""
|
"""
|
||||||
@ -343,43 +344,38 @@ def is_image_mounted(disk_img):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def anaconda_install( disk_img, disk_size, kickstart, repo, args ):
|
class KernelInfo(object):
|
||||||
"""
|
"""
|
||||||
disk_img Full path of the disk image
|
Info about the kernels in boot_dir
|
||||||
disk_size Disk size in GB
|
|
||||||
kickstart Full path to kickstart file
|
|
||||||
repo URL of repository
|
|
||||||
args Extra args to pass to anaconda --image install
|
|
||||||
"""
|
"""
|
||||||
import selinux
|
def __init__(self, boot_dir):
|
||||||
|
self.boot_dir = boot_dir
|
||||||
|
self.list = self.get_kernels()
|
||||||
|
self.arch = self.get_kernel_arch()
|
||||||
|
log.debug("kernel_list for {0.boot_dir} = {0.list}".format(self))
|
||||||
|
log.debug("kernel_arch is {0.arch}".format(self))
|
||||||
|
|
||||||
# Set selinux to Permissive if it is Enforcing
|
def get_kernels(self):
|
||||||
selinux_enforcing = False
|
|
||||||
if selinux.is_selinux_enabled() and selinux.security_getenforce():
|
|
||||||
selinux_enforcing = True
|
|
||||||
selinux.security_setenforce(0)
|
|
||||||
|
|
||||||
# Create the sparse image
|
|
||||||
mksparse( disk_img, disk_size * 1024**3 )
|
|
||||||
|
|
||||||
cmd = [ "anaconda", "--image", disk_img, "--kickstart", kickstart,
|
|
||||||
"--cmdline", "--repo", repo_url ]
|
|
||||||
cmd += args
|
|
||||||
|
|
||||||
rc = execWithRedirect( cmd[0], cmd[1:] )
|
|
||||||
|
|
||||||
if selinux_enforcing:
|
|
||||||
selinux.security_setenforce(1)
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
def get_kernels( boot_dir ):
|
|
||||||
"""
|
"""
|
||||||
|
Get a list of the kernels in the boot_dir
|
||||||
|
|
||||||
Examine the vmlinuz-* versions and return a list of them
|
Examine the vmlinuz-* versions and return a list of them
|
||||||
"""
|
"""
|
||||||
files = os.listdir(boot_dir)
|
files = os.listdir(self.boot_dir)
|
||||||
return [f[8:] for f in files if f.startswith("vmlinuz-")]
|
return [f[8:] for f in files if f.startswith("vmlinuz-")]
|
||||||
|
|
||||||
|
def get_kernel_arch(self):
|
||||||
|
"""
|
||||||
|
Get the arch of the first kernel in boot_dir
|
||||||
|
|
||||||
|
Defaults to i386
|
||||||
|
"""
|
||||||
|
if self.list:
|
||||||
|
kernel_arch = self.list[0].split(".")[-1]
|
||||||
|
else:
|
||||||
|
kernel_arch = "i386"
|
||||||
|
return kernel_arch
|
||||||
|
|
||||||
|
|
||||||
def make_appliance(disk_img, name, template, outfile, networks=None, ram=1024,
|
def make_appliance(disk_img, name, template, outfile, networks=None, ram=1024,
|
||||||
vcpus=1, arch=None, title="Linux", project="Linux",
|
vcpus=1, arch=None, title="Linux", project="Linux",
|
||||||
@ -450,8 +446,32 @@ def make_ami( disk_img, ami_img="ami-root.img", ami_label="AMI" ):
|
|||||||
return work_dir
|
return work_dir
|
||||||
|
|
||||||
|
|
||||||
def make_livecd( disk_img, squashfs_args="", templatedir=None,
|
def make_runtime(opts, mount_dir, work_dir):
|
||||||
title="Linux", project="Linux", releasever=16, isolabel=None ):
|
"""
|
||||||
|
Make the squashfs image from a directory
|
||||||
|
|
||||||
|
Result is in work_dir+RUNTIME
|
||||||
|
"""
|
||||||
|
kernels = KernelInfo(joinpaths(mount_dir, "boot" ))
|
||||||
|
|
||||||
|
# Fake yum object
|
||||||
|
fake_yum = DataHolder(conf=DataHolder(installroot=mount_dir))
|
||||||
|
# Fake arch with only basearch set
|
||||||
|
arch = DataHolder(basearch=kernels.arch, libdir=None, buildarch=None)
|
||||||
|
# TODO: Need to get release info from someplace...
|
||||||
|
product = DataHolder(name=opts.project, version=opts.releasever, release="",
|
||||||
|
variant="", bugurl="", isfinal=False)
|
||||||
|
|
||||||
|
# This is a mounted image partition, cannot hardlink to it, so just use it
|
||||||
|
# symlink mount_dir/images to work_dir/images so we don't run out of space
|
||||||
|
os.makedirs(joinpaths(work_dir, "images"))
|
||||||
|
|
||||||
|
rb = RuntimeBuilder(product, arch, fake_yum)
|
||||||
|
log.info("Creating runtime")
|
||||||
|
rb.create_runtime(joinpaths(work_dir, RUNTIME), size=None)
|
||||||
|
|
||||||
|
|
||||||
|
def make_livecd(opts, mount_dir, work_dir):
|
||||||
"""
|
"""
|
||||||
Take the content from the disk image and make a livecd out of it
|
Take the content from the disk image and make a livecd out of it
|
||||||
|
|
||||||
@ -466,63 +486,37 @@ def make_livecd( disk_img, squashfs_args="", templatedir=None,
|
|||||||
mounts that and then mounts LiveOS/rootfs.img as /
|
mounts that and then mounts LiveOS/rootfs.img as /
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with PartitionMount( disk_img ) as img_mount:
|
kernels = KernelInfo(joinpaths(mount_dir, "boot" ))
|
||||||
if not img_mount or not img_mount.mount_dir:
|
|
||||||
return None
|
|
||||||
|
|
||||||
kernel_list = get_kernels( joinpaths( img_mount.mount_dir, "boot" ) )
|
arch = DataHolder(basearch=kernels.arch, libdir=None, buildarch=None)
|
||||||
log.debug( "kernel_list = {0}".format(kernel_list) )
|
# TODO: Need to get release info from someplace...
|
||||||
if kernel_list:
|
product = DataHolder(name=opts.project, version=opts.releasever, release="",
|
||||||
kernel_arch = kernel_list[0].split(".")[-1]
|
variant="", bugurl="", isfinal=False)
|
||||||
else:
|
|
||||||
kernel_arch = "i386"
|
|
||||||
log.debug( "kernel_arch = {0}".format(kernel_arch) )
|
|
||||||
|
|
||||||
work_dir = tempfile.mkdtemp()
|
# Link /images to work_dir/images to make the templates happy
|
||||||
log.info( "working dir is {0}".format(work_dir) )
|
if os.path.islink(joinpaths(mount_dir, "images")):
|
||||||
|
os.unlink(joinpaths(mount_dir, "images"))
|
||||||
|
execWithRedirect("/bin/ln", ["-s", joinpaths(work_dir, "images"),
|
||||||
|
joinpaths(mount_dir, "images")])
|
||||||
|
|
||||||
runtime = "images/install.img"
|
# The templates expect the config files to be in /tmp/config_files
|
||||||
# This is a mounted image partition, cannot hardlink to it, so just use it
|
|
||||||
installroot = img_mount.mount_dir
|
|
||||||
# symlink installroot/images to work_dir/images so we don't run out of space
|
|
||||||
os.makedirs( joinpaths( work_dir, "images" ) )
|
|
||||||
|
|
||||||
# TreeBuilder expects the config files to be in /tmp/config_files
|
|
||||||
# I think these should be release specific, not from lorax, but for now
|
# I think these should be release specific, not from lorax, but for now
|
||||||
configdir = joinpaths(templatedir,"live/config_files/")
|
configdir = joinpaths(opts.lorax_templates,"live/config_files/")
|
||||||
configdir_path = "tmp/config_files"
|
configdir_path = "tmp/config_files"
|
||||||
fullpath = joinpaths(installroot, configdir_path)
|
fullpath = joinpaths(mount_dir, configdir_path)
|
||||||
if os.path.exists(fullpath):
|
if os.path.exists(fullpath):
|
||||||
remove(fullpath)
|
remove(fullpath)
|
||||||
shutil.copytree(configdir, fullpath)
|
shutil.copytree(configdir, fullpath)
|
||||||
|
|
||||||
# Fake yum object
|
isolabel = opts.volid or "{0.name} {0.version} {1.basearch}".format(product, arch)
|
||||||
fake_yum = DataHolder( conf=DataHolder( installroot=installroot ) )
|
|
||||||
# Fake arch with only basearch set
|
|
||||||
arch = DataHolder( basearch=kernel_arch, libdir=None, buildarch=None )
|
|
||||||
# TODO: Need to get release info from someplace...
|
|
||||||
product = DataHolder( name=project, version=releasever, release="",
|
|
||||||
variant="", bugurl="", isfinal=False )
|
|
||||||
|
|
||||||
rb = RuntimeBuilder( product, arch, fake_yum )
|
|
||||||
log.info( "Creating runtime" )
|
|
||||||
rb.create_runtime( joinpaths( work_dir, runtime ), size=None )
|
|
||||||
|
|
||||||
# Link /images to work_dir/images to make the templates happy
|
|
||||||
if os.path.islink( joinpaths( installroot, "images" ) ):
|
|
||||||
os.unlink( joinpaths( installroot, "images" ) )
|
|
||||||
execWithRedirect("/bin/ln", ["-s", joinpaths(work_dir, "images"),
|
|
||||||
joinpaths(installroot, "images")])
|
|
||||||
|
|
||||||
isolabel = isolabel or "{0.name} {0.version} {1.basearch}".format(product, arch)
|
|
||||||
if len(isolabel) > 32:
|
if len(isolabel) > 32:
|
||||||
isolabel = isolabel[:32]
|
isolabel = isolabel[:32]
|
||||||
log.error("Truncating isolabel to 32 chars: %s" % (isolabel,))
|
log.warn("Truncating isolabel to 32 chars: %s" % (isolabel,))
|
||||||
|
|
||||||
tb = TreeBuilder( product=product, arch=arch,
|
tb = TreeBuilder(product=product, arch=arch,
|
||||||
inroot=installroot, outroot=work_dir,
|
inroot=mount_dir, outroot=work_dir,
|
||||||
runtime=runtime, isolabel=isolabel,
|
runtime=RUNTIME, isolabel=isolabel,
|
||||||
templatedir=joinpaths(templatedir,"live/"))
|
templatedir=joinpaths(opts.lorax_templates,"live/"))
|
||||||
log.info( "Rebuilding initrds" )
|
log.info( "Rebuilding initrds" )
|
||||||
if not opts.dracut_args:
|
if not opts.dracut_args:
|
||||||
dracut_args = DRACUT_DEFAULT
|
dracut_args = DRACUT_DEFAULT
|
||||||
@ -530,14 +524,159 @@ def make_livecd( disk_img, squashfs_args="", templatedir=None,
|
|||||||
dracut_args = []
|
dracut_args = []
|
||||||
for arg in opts.dracut_args:
|
for arg in opts.dracut_args:
|
||||||
dracut_args += arg.split(" ", 1)
|
dracut_args += arg.split(" ", 1)
|
||||||
log.info( "dracut args = {0}".format( dracut_args ) )
|
log.info("dracut args = {0}".format(dracut_args))
|
||||||
tb.rebuild_initrds( add_args=dracut_args )
|
tb.rebuild_initrds(add_args=dracut_args)
|
||||||
log.info( "Building boot.iso" )
|
log.info("Building boot.iso")
|
||||||
tb.build()
|
tb.build()
|
||||||
|
|
||||||
return work_dir
|
return work_dir
|
||||||
|
|
||||||
|
|
||||||
|
def novirt_install(opts, disk_img, disk_size, repo_url):
|
||||||
|
"""
|
||||||
|
Use Anaconda to install to a disk image
|
||||||
|
"""
|
||||||
|
import selinux
|
||||||
|
|
||||||
|
# Set selinux to Permissive if it is Enforcing
|
||||||
|
selinux_enforcing = False
|
||||||
|
if selinux.is_selinux_enabled() and selinux.security_getenforce():
|
||||||
|
selinux_enforcing = True
|
||||||
|
selinux.security_setenforce(0)
|
||||||
|
|
||||||
|
args = ["--kickstart", opts.ks[0], "--cmdline", "--repo", repo_url]
|
||||||
|
if opts.anaconda_args:
|
||||||
|
for arg in opts.anaconda_args:
|
||||||
|
args += arg.split(" ", 1)
|
||||||
|
if opts.proxy:
|
||||||
|
args += ["--proxy", opts.proxy]
|
||||||
|
if opts.armplatform:
|
||||||
|
args += ["--armplatform", opts.armplatform]
|
||||||
|
|
||||||
|
if opts.make_iso or opts.make_fsimage:
|
||||||
|
# Make a blank fs image
|
||||||
|
args += ["--dirinstall"]
|
||||||
|
|
||||||
|
mkext4img(None, disk_img, label="Anaconda", size=disk_size * 1024**3)
|
||||||
|
if not os.path.isdir(ROOT_PATH):
|
||||||
|
os.mkdir(ROOT_PATH)
|
||||||
|
mount(disk_img, opts="loop", mnt=ROOT_PATH)
|
||||||
|
else:
|
||||||
|
args += ["--image", disk_img]
|
||||||
|
|
||||||
|
# Create the sparse image
|
||||||
|
mksparse(disk_img, disk_size * 1024**3)
|
||||||
|
|
||||||
|
rc = execWithRedirect("anaconda", args)
|
||||||
|
|
||||||
|
# Move the anaconda logs over to a log directory
|
||||||
|
log_dir = os.path.abspath(os.path.dirname(opts.logfile))
|
||||||
|
log_anaconda = joinpaths(log_dir, "anaconda")
|
||||||
|
if not os.path.isdir(log_anaconda):
|
||||||
|
os.mkdir(log_anaconda)
|
||||||
|
for l in ["anaconda.log", "ifcfg.log", "program.log", "storage.log",
|
||||||
|
"packaging.log", "yum.log"]:
|
||||||
|
if os.path.exists("/tmp/"+l):
|
||||||
|
shutil.copy2("/tmp/"+l, log_anaconda)
|
||||||
|
os.unlink("/tmp/"+l)
|
||||||
|
|
||||||
|
if opts.make_iso or opts.make_fsimage:
|
||||||
|
umount(ROOT_PATH)
|
||||||
|
else:
|
||||||
|
# If anaconda failed the disk image may still be in use by dm
|
||||||
|
execWithRedirect("anaconda-cleanup", [])
|
||||||
|
dm_name = os.path.splitext(os.path.basename(disk_img))[0]
|
||||||
|
dm_path = "/dev/mapper/"+dm_name
|
||||||
|
if os.path.exists(dm_path):
|
||||||
|
dm_detach(dm_path)
|
||||||
|
loop_detach(get_loop_name(disk_img))
|
||||||
|
|
||||||
|
if selinux_enforcing:
|
||||||
|
selinux.security_setenforce(1)
|
||||||
|
|
||||||
|
if rc:
|
||||||
|
raise InstallError("novirt_install failed")
|
||||||
|
|
||||||
|
|
||||||
|
def virt_install(opts, install_log, disk_img, disk_size):
|
||||||
|
"""
|
||||||
|
Use virt-install to install to a disk image
|
||||||
|
"""
|
||||||
|
iso_mount = IsoMountpoint(opts.iso, opts.location)
|
||||||
|
log_monitor = LogMonitor(install_log)
|
||||||
|
|
||||||
|
kernel_args = ""
|
||||||
|
if opts.kernel_args:
|
||||||
|
kernel_args += opts.kernel_args
|
||||||
|
if opts.proxy:
|
||||||
|
kernel_args += " proxy="+opts.proxy
|
||||||
|
|
||||||
|
virt = VirtualInstall(iso_mount, opts.ks, disk_img, disk_size,
|
||||||
|
kernel_args, opts.ram, opts.vnc, opts.arch,
|
||||||
|
log_check = log_monitor.server.log_check,
|
||||||
|
virtio_host = log_monitor.host,
|
||||||
|
virtio_port = log_monitor.port)
|
||||||
|
virt.destroy()
|
||||||
|
log_monitor.shutdown()
|
||||||
|
iso_mount.umount()
|
||||||
|
|
||||||
|
if log_monitor.server.log_check():
|
||||||
|
raise InstallError("virt_install failed")
|
||||||
|
|
||||||
|
|
||||||
|
def make_squashfs(disk_img, work_dir, compression="xz"):
|
||||||
|
"""
|
||||||
|
Take disk_img and put it into LiveOS/rootfs.img and squashfs this
|
||||||
|
tree into work_dir+RUNTIME
|
||||||
|
"""
|
||||||
|
liveos_dir = joinpaths(work_dir, "runtime/LiveOS")
|
||||||
|
os.makedirs(liveos_dir)
|
||||||
|
os.makedirs(os.path.dirname(joinpaths(work_dir, RUNTIME)))
|
||||||
|
execWithRedirect("/bin/ln", [disk_img,
|
||||||
|
joinpaths(liveos_dir, "rootfs.img")])
|
||||||
|
mksquashfs(joinpaths(work_dir, "runtime"),
|
||||||
|
joinpaths(work_dir, RUNTIME), compression)
|
||||||
|
remove(joinpaths(work_dir, "runtime"))
|
||||||
|
|
||||||
|
|
||||||
|
def make_image(opts, ks):
|
||||||
|
"""
|
||||||
|
Install to an image
|
||||||
|
|
||||||
|
Use virt or anaconda to install to an image.
|
||||||
|
|
||||||
|
Returns the name of the image created.
|
||||||
|
"""
|
||||||
|
disk_size = 1 + (sum([p.size for p in ks.handler.partition.partitions]) / 1024)
|
||||||
|
log.info("disk_size = {0}GB".format(disk_size))
|
||||||
|
|
||||||
|
repo_url = ks.handler.method.url
|
||||||
|
|
||||||
|
if opts.image_name:
|
||||||
|
disk_img = joinpaths(opts.tmp, opts.image_name)
|
||||||
|
else:
|
||||||
|
disk_img = tempfile.mktemp(prefix="disk", suffix=".img", dir=opts.tmp)
|
||||||
|
install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
|
||||||
|
|
||||||
|
log.info("disk_img = {0}".format(disk_img))
|
||||||
|
log.info("install_log = {0}".format(install_log))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if opts.no_virt:
|
||||||
|
novirt_install(opts, disk_img, disk_size, repo_url)
|
||||||
|
else:
|
||||||
|
virt_install(opts, install_log, disk_img, disk_size)
|
||||||
|
except InstallError as e:
|
||||||
|
log.error("Install failed: {0}".format(e))
|
||||||
|
if not opts.keep_image:
|
||||||
|
log.info("Removing bad disk image")
|
||||||
|
os.unlink(disk_img)
|
||||||
|
raise
|
||||||
|
|
||||||
|
log.info("Disk Image install successful")
|
||||||
|
return disk_img
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(opts):
|
def setup_logging(opts):
|
||||||
# Setup logging to console and to logfile
|
# Setup logging to console and to logfile
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
@ -574,7 +713,9 @@ if __name__ == '__main__':
|
|||||||
action.add_argument( "--make-iso", action="store_true",
|
action.add_argument( "--make-iso", action="store_true",
|
||||||
help="Build a live iso" )
|
help="Build a live iso" )
|
||||||
action.add_argument( "--make-disk", action="store_true",
|
action.add_argument( "--make-disk", action="store_true",
|
||||||
help="Build a disk image" )
|
help="Build a partitioned disk image" )
|
||||||
|
action.add_argument( "--make-fsimage", action="store_true",
|
||||||
|
help="Build a filesystem image" )
|
||||||
action.add_argument( "--make-appliance", action="store_true",
|
action.add_argument( "--make-appliance", action="store_true",
|
||||||
help="Build an appliance image and XML description" )
|
help="Build an appliance image and XML description" )
|
||||||
action.add_argument( "--make-ami", action="store_true",
|
action.add_argument( "--make-ami", action="store_true",
|
||||||
@ -584,13 +725,15 @@ if __name__ == '__main__':
|
|||||||
help="Anaconda installation .iso path to use for virt-install" )
|
help="Anaconda installation .iso path to use for virt-install" )
|
||||||
parser.add_argument( "--disk-image", type=os.path.abspath,
|
parser.add_argument( "--disk-image", type=os.path.abspath,
|
||||||
help="Path to disk image to use for creating final image" )
|
help="Path to disk image to use for creating final image" )
|
||||||
|
parser.add_argument( "--fs-image", type=os.path.abspath,
|
||||||
|
help="Path to filesystem image to use for creating final image" )
|
||||||
|
|
||||||
parser.add_argument( "--ks", action="append", type=os.path.abspath,
|
parser.add_argument( "--ks", action="append", type=os.path.abspath,
|
||||||
help="Kickstart file defining the install." )
|
help="Kickstart file defining the install." )
|
||||||
parser.add_argument( "--image-name", default=None,
|
parser.add_argument( "--image-name", default=None,
|
||||||
help="Name of disk image to create. Default is a random name." )
|
help="Name of fs/disk image to create. Default is a random name." )
|
||||||
parser.add_argument( "--image-only", action="store_true",
|
parser.add_argument( "--image-only", action="store_true",
|
||||||
help="Exit after creating disk image." )
|
help="Exit after creating fs/disk image." )
|
||||||
parser.add_argument( "--keep-image", action="store_true",
|
parser.add_argument( "--keep-image", action="store_true",
|
||||||
help="Keep raw disk image after .iso creation" )
|
help="Keep raw disk image after .iso creation" )
|
||||||
parser.add_argument( "--no-virt", action="store_true",
|
parser.add_argument( "--no-virt", action="store_true",
|
||||||
@ -692,23 +835,30 @@ if __name__ == '__main__':
|
|||||||
log.error( "The disk image {0} is missing.".format( opts.disk_image ) )
|
log.error( "The disk image {0} is missing.".format( opts.disk_image ) )
|
||||||
sys.exit( 1 )
|
sys.exit( 1 )
|
||||||
|
|
||||||
if not opts.no_virt and not opts.iso and not opts.disk_image:
|
if opts.fs_image and not os.path.exists( opts.fs_image ):
|
||||||
|
log.error( "The filesystem image {0} is missing.".format( opts.fs_image ) )
|
||||||
|
sys.exit( 1 )
|
||||||
|
|
||||||
|
is_install = not (opts.disk_image or opts.fs_image)
|
||||||
|
if is_install and not opts.no_virt and not opts.iso:
|
||||||
log.error( "virt-install needs an install iso." )
|
log.error( "virt-install needs an install iso." )
|
||||||
sys.exit( 1 )
|
sys.exit( 1 )
|
||||||
|
|
||||||
if opts.volid and len(opts.volid) > 32:
|
if opts.volid and len(opts.volid) > 32:
|
||||||
logger.fatal("the volume id cannot be longer than 32 characters")
|
log.fatal("the volume id cannot be longer than 32 characters")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not opts.no_virt and not libvirt:
|
if is_install and not opts.no_virt and not libvirt:
|
||||||
log.error("virt-install requires libvirt-python to be installed.")
|
log.error("virt-install requires libvirt-python to be installed.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not opts.no_virt and not os.path.exists("/usr/bin/virt-install"):
|
if is_install and not opts.no_virt \
|
||||||
|
and not os.path.exists("/usr/bin/virt-install"):
|
||||||
log.error("virt-install requires python-virtinst to be installed.")
|
log.error("virt-install requires python-virtinst to be installed.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if opts.no_virt and not os.path.exists("/usr/sbin/anaconda"):
|
if not is_install and opts.no_virt \
|
||||||
|
and not os.path.exists("/usr/sbin/anaconda"):
|
||||||
log.error("no-virt requires anaconda to be installed.")
|
log.error("no-virt requires anaconda to be installed.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@ -721,7 +871,7 @@ if __name__ == '__main__':
|
|||||||
"exist".format(opts.app_template))
|
"exist".format(opts.app_template))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if opts.image_name and os.path.exists(joinpaths(opts.tmp,opts.image_name)):
|
if opts.image_name and os.path.exists(joinpaths(opts.tmp, opts.image_name)):
|
||||||
log.error("The disk image to be created should not exist.")
|
log.error("The disk image to be created should not exist.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@ -737,112 +887,63 @@ if __name__ == '__main__':
|
|||||||
ks = KickstartParser( ks_version, errorsAreFatal=False, missingIncludeIsFatal=False )
|
ks = KickstartParser( ks_version, errorsAreFatal=False, missingIncludeIsFatal=False )
|
||||||
ks.readKickstart( opts.ks[0] )
|
ks.readKickstart( opts.ks[0] )
|
||||||
|
|
||||||
# Make the disk image
|
# Make the disk or filesystem image
|
||||||
if not opts.disk_image:
|
if not opts.disk_image and not opts.fs_image:
|
||||||
if not opts.ks:
|
if not opts.ks:
|
||||||
log.error("Image creation requires a kickstart file")
|
log.error("Image creation requires a kickstart file")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
disk_size = 1 + (sum( [p.size for p in ks.handler.partition.partitions] ) / 1024)
|
errors = []
|
||||||
log.info( "disk_size = {0}GB".format(disk_size) )
|
|
||||||
|
|
||||||
if ks.handler.method.method != "url":
|
if ks.handler.method.method != "url":
|
||||||
log.error( "Only url install method is currently supported. Please "
|
errors.append("Only url install method is currently supported. Please "
|
||||||
"fix your kickstart file." )
|
"fix your kickstart file." )
|
||||||
sys.exit( 1 )
|
|
||||||
repo_url = ks.handler.method.url
|
|
||||||
if ks.handler.displaymode.displayMode is not None:
|
if ks.handler.displaymode.displayMode is not None:
|
||||||
log.error("The kickstart must not set a display mode (text, cmdline, "
|
errors.append("The kickstart must not set a display mode (text, cmdline, "
|
||||||
"graphical), this will interfere with livemedia-creator.")
|
"graphical), this will interfere with livemedia-creator.")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
for e in errors:
|
||||||
|
log.error(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if opts.image_name:
|
# Make the image. Output of this is either a partitioned disk image or a fsimage
|
||||||
disk_img = joinpaths(opts.tmp, opts.image_name)
|
try:
|
||||||
else:
|
disk_img = make_image(opts, ks)
|
||||||
disk_img = tempfile.mktemp( prefix="disk", suffix=".img", dir=opts.tmp )
|
except InstallError as e:
|
||||||
install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
|
log.error("ERROR: Image creation failed: %s" % (e))
|
||||||
|
sys.exit(1)
|
||||||
log.info( "disk_img = {0}".format(disk_img) )
|
|
||||||
log.info( "install_log = {0}".format(install_log) )
|
|
||||||
|
|
||||||
if opts.no_virt:
|
|
||||||
anaconda_args = []
|
|
||||||
if opts.anaconda_args:
|
|
||||||
for arg in opts.anaconda_args:
|
|
||||||
anaconda_args += arg.split(" ", 1)
|
|
||||||
if opts.proxy:
|
|
||||||
anaconda_args += [ "--proxy", opts.proxy ]
|
|
||||||
if opts.armplatform:
|
|
||||||
anaconda_args += [ "--armplatform", opts.armplatform ]
|
|
||||||
|
|
||||||
# Use anaconda's image install
|
|
||||||
install_error = anaconda_install( disk_img, disk_size, opts.ks[0],
|
|
||||||
repo_url, anaconda_args )
|
|
||||||
|
|
||||||
# Move the anaconda logs over to a log directory
|
|
||||||
log_dir = os.path.abspath(os.path.dirname(opts.logfile))
|
|
||||||
log_anaconda = joinpaths( log_dir, "anaconda" )
|
|
||||||
if not os.path.isdir( log_anaconda ):
|
|
||||||
os.mkdir( log_anaconda )
|
|
||||||
for l in ["anaconda.log", "ifcfg.log", "program.log", "storage.log",
|
|
||||||
"packaging.log", "yum.log"]:
|
|
||||||
if os.path.exists( "/tmp/"+l ):
|
|
||||||
shutil.copy2( "/tmp/"+l, log_anaconda )
|
|
||||||
os.unlink( "/tmp/"+l )
|
|
||||||
|
|
||||||
# If anaconda failed the disk image may still be in use by dm
|
|
||||||
execWithRedirect("anaconda-cleanup", [])
|
|
||||||
dm_name = os.path.splitext(os.path.basename(disk_img))[0]
|
|
||||||
dm_path = "/dev/mapper/"+dm_name
|
|
||||||
if os.path.exists(dm_path):
|
|
||||||
dm_detach(dm_path)
|
|
||||||
loop_detach(get_loop_name(disk_img))
|
|
||||||
|
|
||||||
else:
|
|
||||||
iso_mount = IsoMountpoint( opts.iso, opts.location )
|
|
||||||
log_monitor = LogMonitor( install_log )
|
|
||||||
|
|
||||||
kernel_args = ""
|
|
||||||
if opts.kernel_args:
|
|
||||||
kernel_args += opts.kernel_args
|
|
||||||
if opts.proxy:
|
|
||||||
kernel_args += " proxy="+opts.proxy
|
|
||||||
|
|
||||||
virt = VirtualInstall( iso_mount, opts.ks, disk_img, disk_size,
|
|
||||||
kernel_args, opts.ram, opts.vnc, opts.arch,
|
|
||||||
log_check = log_monitor.server.log_check,
|
|
||||||
virtio_host = log_monitor.host,
|
|
||||||
virtio_port = log_monitor.port )
|
|
||||||
virt.destroy()
|
|
||||||
log_monitor.shutdown()
|
|
||||||
iso_mount.umount()
|
|
||||||
|
|
||||||
install_error = log_monitor.server.log_check()
|
|
||||||
|
|
||||||
if install_error:
|
|
||||||
log.error( "Install failed" )
|
|
||||||
if not opts.keep_image:
|
|
||||||
log.info( "Removing bad disk image" )
|
|
||||||
os.unlink( disk_img )
|
|
||||||
sys.exit( 1 )
|
|
||||||
else:
|
|
||||||
log.info( "Disk Image install successful" )
|
|
||||||
|
|
||||||
|
|
||||||
|
if not opts.image_only:
|
||||||
result_dir = None
|
result_dir = None
|
||||||
if opts.make_iso and not opts.image_only:
|
if opts.make_iso:
|
||||||
result_dir = make_livecd( opts.disk_image or disk_img, opts.squashfs_args,
|
work_dir = tempfile.mkdtemp()
|
||||||
opts.lorax_templates,
|
log.info("working dir is {0}".format(work_dir))
|
||||||
opts.title, opts.project, opts.releasever,
|
|
||||||
opts.volid )
|
if (opts.fs_image or opts.no_virt) and not opts.disk_image:
|
||||||
|
# Create iso from a filesystem image
|
||||||
|
disk_img = opts.fs_image or disk_img
|
||||||
|
|
||||||
|
make_squashfs(disk_img, work_dir)
|
||||||
|
with Mount(disk_img, opts="loop") as mount_dir:
|
||||||
|
result_dir = make_livecd(opts, mount_dir, work_dir)
|
||||||
|
else:
|
||||||
|
# Create iso from a partitioned disk image
|
||||||
|
disk_img = opts.disk_image or disk_img
|
||||||
|
with PartitionMount(disk_img) as img_mount:
|
||||||
|
if img_mount and img_mount.mount_dir:
|
||||||
|
make_runtime(opts, img_mount.mount_dir, work_dir)
|
||||||
|
result_dir = make_livecd(opts, img_mount.mount_dir, work_dir)
|
||||||
|
|
||||||
# cleanup the mess
|
# cleanup the mess
|
||||||
if disk_img and not opts.keep_image and not opts.disk_image:
|
# cleanup work_dir?
|
||||||
os.unlink( disk_img )
|
if disk_img and not (opts.keep_image or opts.disk_image or opts.fs_image):
|
||||||
|
os.unlink(disk_img)
|
||||||
log.info("Disk image erased")
|
log.info("Disk image erased")
|
||||||
disk_img = None
|
disk_img = None
|
||||||
elif opts.make_ami and not opts.image_only:
|
elif opts.make_ami:
|
||||||
result_dir = make_ami(opts.disk_image or disk_img)
|
result_dir = make_ami(opts.disk_image or disk_img)
|
||||||
elif opts.make_appliance and not opts.image_only:
|
elif opts.make_appliance:
|
||||||
if not opts.ks:
|
if not opts.ks:
|
||||||
networks = []
|
networks = []
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user