livemedia-creator: Use qemu instead of virt-install
Switching to using qemu directly allows lmc to be more flexible. It can now run from inside a mock chroot for creation of all image types, inculding disk images, and can take advantage of KVM on the host system if /dev/kvm device is present inside the mock. It should also be possible to create cross-arch images, but without kvm available this is likely to be a very slow option.
This commit is contained in:
parent
8e47b8a96a
commit
f8316a7b89
@ -226,7 +226,7 @@ Passed to --vcpus command
|
||||
|
||||
.TP
|
||||
\fB\-\-vnc VNC\fR
|
||||
Passed to --graphics command
|
||||
Passed to qemu -display command. eg. vnc=127.0.0.1:5, default is to choose the first unused vnc port.
|
||||
|
||||
.TP
|
||||
\fB\-\-arch ARCH\fR
|
||||
|
@ -12,8 +12,8 @@ isos, bootable (partitioned) disk images, tarfiles, and filesystem images for
|
||||
use with virtualization and container solutions like libvirt, docker, and
|
||||
OpenStack.
|
||||
|
||||
The general idea is to use virt-install with kickstart and an Anaconda boot.iso
|
||||
to install into a disk image and then use the disk image to create the bootable
|
||||
The general idea is to use qemu with kickstart and an Anaconda boot.iso to
|
||||
install into a disk image and then use the disk image to create the bootable
|
||||
media.
|
||||
|
||||
livemedia-creator --help will describe all of the options available. At the
|
||||
@ -21,19 +21,14 @@ minimum you need:
|
||||
|
||||
``--make-iso`` to create a final bootable .iso or one of the other ``--make-*`` options.
|
||||
|
||||
``--iso`` to specify the Anaconda install media to use with virt-install
|
||||
``--iso`` to specify the Anaconda install media to use with qemu.
|
||||
|
||||
``--ks`` to select the kickstart file describing what to install.
|
||||
|
||||
To use livemedia-creator with virt-install you will need to install the
|
||||
following packages, as well as have libvirtd setup correctly.
|
||||
|
||||
* ``virt-install``
|
||||
* ``libvirt-python``
|
||||
To use livemedia-creator with virtualization you will need to have qemu installed.
|
||||
|
||||
If you are going to be using Anaconda directly, with ``--no-virt`` mode, make sure
|
||||
you have the anaconda package installed. You can use the anaconda-tui package
|
||||
to save a bit of space on the build system.
|
||||
you have the anaconda-tui package installed.
|
||||
|
||||
Conventions used in this document:
|
||||
|
||||
@ -58,8 +53,8 @@ You can run it directly from the lorax git repo like this::
|
||||
--make-iso --iso=/extra/iso/boot.iso \
|
||||
--ks=./docs/fedora-livemedia.ks --lorax-templates=./share/
|
||||
|
||||
If you want to watch the install you can pass ``--vnc vnc`` and use a vnc client
|
||||
to connect to localhost:0
|
||||
You can observe the installation using vnc. The logs will show what port was
|
||||
chosen, or you can use a specific port by passing it. eg. ``--vnc vnc:127.0.0.1:5``
|
||||
|
||||
This is usually a good idea when testing changes to the kickstart. lmc tries
|
||||
to monitor the logs for fatal errors, but may not catch everything.
|
||||
@ -74,7 +69,7 @@ Normally you would run both stages, but it is possible to stop after the
|
||||
install stage, by using ``--image-only``, or to skip the install stage and use
|
||||
a previously created disk image by passing ``--disk-image`` or ``--fs-image``
|
||||
|
||||
When creating an iso virt-install boots using the passed Anaconda installer iso
|
||||
When creating an iso qemu 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.
|
||||
@ -82,24 +77,22 @@ current spin-kickstarts do.
|
||||
livemedia-creator monitors the install process for problems by watching the
|
||||
install logs. They are written to the current directory or to the base
|
||||
directory specified by the --logfile command. You can also monitor the install
|
||||
by passing ``--vnc vnc`` and using a vnc client. This is recommended when first
|
||||
modifying a kickstart, since there are still places where Anaconda may get
|
||||
stuck without the log monitor catching it.
|
||||
by using a vnc client. This is recommended when first modifying a kickstart,
|
||||
since there are still places where Anaconda may get stuck without the log
|
||||
monitor catching it.
|
||||
|
||||
The output from this process is a partitioned disk image. kpartx can be used
|
||||
to mount and examine it when there is a problem with the install. It can also
|
||||
be booted using kvm.
|
||||
|
||||
When creating an iso the disk image's / partition is copied into a formatted
|
||||
disk image which is then used as the input to lorax for creation of the final
|
||||
media.
|
||||
filesystem image which is then used as the input to lorax for creation of the
|
||||
final media.
|
||||
|
||||
The final image is created by lorax, using the templates in /usr/share/lorax/
|
||||
or the directory specified by ``--lorax-templates``
|
||||
|
||||
Currently the standard lorax templates are used to make a bootable iso, but
|
||||
it should be possible to modify them to output other results. They are
|
||||
written using the Mako template system which is very flexible.
|
||||
The final image is created by lorax, using the templates in /usr/share/lorax/live/
|
||||
or the live directory below the directory specified by ``--lorax-templates``. The
|
||||
templates are written using the Mako template system with some extra commands
|
||||
added by lorax.
|
||||
|
||||
.. note::
|
||||
The output from --make-iso includes the artifacts used to create the boot.iso;
|
||||
@ -163,10 +156,10 @@ changes. Here are the steps I used to convert the Fedora XFCE spin.
|
||||
memtest86+
|
||||
syslinux
|
||||
|
||||
One drawback to using virt-install is that it pulls the packages from
|
||||
the repo each time you run it. To speed things up you either need a local
|
||||
mirror of the packages, or you can use a caching proxy. When using a proxy
|
||||
you pass it to livemedia-creator like this:
|
||||
One drawback to using qemu is that it pulls the packages from the repo each
|
||||
time you run it. To speed things up you either need a local mirror of the
|
||||
packages, or you can use a caching proxy. When using a proxy you pass it to
|
||||
livemedia-creator like this:
|
||||
|
||||
``--proxy=http://proxy.yourdomain.com:3128``
|
||||
|
||||
@ -175,16 +168,16 @@ packages will get cached, so your kickstart url would look like:
|
||||
|
||||
``url --url="http://dl.fedoraproject.org/pub/fedora/linux/development/rawhide/x86_64/os/"``
|
||||
|
||||
You can also add an update repo, but don't name it updates. Add --proxy to
|
||||
it as well.
|
||||
You can also add an update repo, but don't name it updates. Add --proxy to it
|
||||
as well.
|
||||
|
||||
|
||||
Anaconda image install (no-virt)
|
||||
--------------------------------
|
||||
|
||||
You can create images without using virt-install by passing ``--no-virt`` on the
|
||||
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:
|
||||
You can create images without using qemu by passing ``--no-virt`` on the
|
||||
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:
|
||||
|
||||
1. It will be most reliable when building images for the same release that the
|
||||
host is running. Because Anaconda has expectations about the system it is
|
||||
@ -302,9 +295,9 @@ You can also create qcow2 appliance images using ``--image-type=qcow2``, for exa
|
||||
Filesystem Image Creation
|
||||
-------------------------
|
||||
|
||||
livemedia-creator can be used to create un-partitined filesystem images using the
|
||||
``--make-fsimage`` option. As of version 21.8 this works with both virt-install and no-virt modes
|
||||
of operation. Previously it was only available with no-virt.
|
||||
livemedia-creator can be used to create un-partitined filesystem images using
|
||||
the ``--make-fsimage`` option. As of version 21.8 this works with both qemu and
|
||||
no-virt modes of operation. Previously it was only available with no-virt.
|
||||
|
||||
Kickstarts should have a single / partition with no extra mountpoints.
|
||||
|
||||
@ -347,8 +340,8 @@ using Atomic installer iso with local repo included in the image can be found
|
||||
in docs/rhel-atomic-pxe-live.ks.
|
||||
|
||||
|
||||
Using Mock to Create Images
|
||||
---------------------------
|
||||
Using Mock and --no-virt to Create Images
|
||||
-----------------------------------------
|
||||
|
||||
As of lorax version 22.2 you can use livemedia-creator and anaconda version
|
||||
22.15 inside of a mock chroot with --make-iso and --make-fsimage.
|
||||
@ -404,6 +397,66 @@ located at ~/results/try-1/images/boot.iso, and the ~/results/try-1/
|
||||
directory tree will also contain the vmlinuz, initrd, etc.
|
||||
|
||||
|
||||
Using Mock and qemu to Create Images
|
||||
------------------------------------
|
||||
|
||||
Version 25.0 of livemedia-creator switches to using qemu for virtualization.
|
||||
This allows creation of all image types, and use of the KVM on the host if
|
||||
/dev/kvm is present in the mock environment.
|
||||
|
||||
On the host system:
|
||||
|
||||
1. yum install -y mock
|
||||
|
||||
2. Add a user to the mock group to use for running mock. eg. builder
|
||||
|
||||
3. Create a new /etc/mock/ config file based on the rawhide one, or modify the
|
||||
existing one so that the following options are setup::
|
||||
|
||||
config_opts['chroot_setup_cmd'] = 'install @buildsys-build lorax qemu'
|
||||
|
||||
# build results go into /home/builder/results/
|
||||
config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/home/builder/results','/results/'))
|
||||
|
||||
If you are creating images for a branched release of Fedora you should also enable
|
||||
the updates-testing repository so that you get the latest builds in your mock chroot.
|
||||
|
||||
The following steps are run as the builder user who is a member of the mock
|
||||
group.
|
||||
|
||||
4. Make a directory for results matching the bind mount above
|
||||
``mkdir ~/results/``
|
||||
|
||||
5. Copy the example kickstarts
|
||||
``cp /usr/share/docs/lorax/*ks .``
|
||||
|
||||
6. Make sure tar and dracut-network are in the %packages section and that the
|
||||
``url points to the correct repo``
|
||||
|
||||
7. Init the mock
|
||||
``mock -r fedora-rawhide-x86_64 --init``
|
||||
|
||||
8. Copy the kickstart inside the mock
|
||||
``mock -r fedora-rawhide-x86_64 --copyin ./fedora-minimal.ks /root/``
|
||||
|
||||
9. Copy the Anaconda boot.iso inside the mock
|
||||
``mock -r fedora-rawhide-x86_64 --copyin ./boot.iso /root/``
|
||||
|
||||
10. Make a minimal iso::
|
||||
|
||||
mock -r fedora-rawhide-x86_64 --chroot -- livemedia-creator \
|
||||
--resultdir=/results/try-1 --logfile=/results/logs/try-1/try-1.log \
|
||||
--make-iso --ks /root/fedora-minimal.ks --iso /root/boot.iso
|
||||
|
||||
Results will be in ./results/try-1 and logs under /results/logs/try-1/
|
||||
including anaconda logs and livemedia-creator logs. The new iso will be
|
||||
located at ~/results/try-1/images/boot.iso, and the ~/results/try-1/
|
||||
directory tree will also contain the vmlinuz, initrd, etc.
|
||||
|
||||
This will run qemu without kvm support, which is going to be very slow. You can
|
||||
add ``mknod /dev/kvm c 10 232;`` to create the device node before running lmc.
|
||||
|
||||
|
||||
OpenStack Image Creation
|
||||
------------------------
|
||||
|
||||
@ -528,29 +581,27 @@ Make sure that the kickstart you are using creates a /boot/efi partition by incl
|
||||
Debugging problems
|
||||
------------------
|
||||
|
||||
Sometimes an installation will get stuck. When using virt-install the logs will
|
||||
Sometimes an installation will get stuck. When using qemu the logs will
|
||||
be written to ./virt-install.log and most of the time any problems that happen
|
||||
will be near the end of the file. lmc tries to detect common errors and will
|
||||
cancel the installation when they happen. But not everything can be caught.
|
||||
When creating a new kickstart it is helpful to use the ``--vnc vnc`` command so
|
||||
that you can monitor the installation as it happens, and if it gets stuck
|
||||
without lmc detecting the problem you can switch to tty1 and examine the system
|
||||
directly.
|
||||
When creating a new kickstart it is helpful to use vnc so that you can monitor
|
||||
the installation as it happens, and if it gets stuck without lmc detecting the
|
||||
problem you can switch to tty1 and examine the system directly.
|
||||
|
||||
If it does get stuck the best way to cancel is to use virsh to destroy the domain.
|
||||
|
||||
1. Use ``sudo virsh list`` to show the name of the virt. It will start with LiveOS and contain a UUID.
|
||||
2. Run ``sudo virsh destroy <name>`` to destroy the domain.
|
||||
3. Wait 20 seconds or so for lmc to detect that the domain vanished. It should handle cleanup.
|
||||
If it does get stuck the best way to cancel is to use kill -9 on the qemu pid,
|
||||
lmc will detect that the process died and cleanup.
|
||||
|
||||
If lmc didn't handle the cleanup for some reason you can do this:
|
||||
1. ``sudo virsh undefine <name>``
|
||||
2. ``sudo umount /tmp/tmpXXXX`` to unmount the iso from its mountpoint.
|
||||
3. ``sudo rm -rf /tmp/tmpXXXX``
|
||||
4. ``sudo rm /var/tmp/diskXXXXX`` to remove the disk image.
|
||||
1. ``sudo umount /tmp/lmc-XXXX`` to unmount the iso from its mountpoint.
|
||||
2. ``sudo rm -rf /tmp/lmc-XXXX``
|
||||
3. ``sudo rm /var/tmp/lmc-disk-XXXXX`` to remove the disk image.
|
||||
|
||||
The logs from the virt-install run are stored in virt-install.log,
|
||||
logs from livemedia-creator are in livemedia.log and program.log
|
||||
Note that lmc uses the lmc- prefix for all of its temporary files and
|
||||
directories to make it easier to find and clean up leftovers.
|
||||
|
||||
The logs from the qemu run are stored in virt-install.log, logs from
|
||||
livemedia-creator are in livemedia.log and program.log
|
||||
|
||||
You can add ``--image-only`` to skip the .iso creation and examine the resulting
|
||||
disk image. Or you can pass ``--keep-image`` to keep it around after the iso has
|
||||
|
@ -24,16 +24,15 @@ log = logging.getLogger("livemedia-creator")
|
||||
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
import tempfile
|
||||
import subprocess
|
||||
from time import sleep
|
||||
import shutil
|
||||
import argparse
|
||||
import hashlib
|
||||
import glob
|
||||
import json
|
||||
from math import ceil
|
||||
import socket
|
||||
|
||||
# Use pykickstart to calculate disk image size
|
||||
from pykickstart.parser import KickstartParser
|
||||
@ -53,17 +52,11 @@ from pylorax.sysutils import joinpaths, remove
|
||||
from pylorax.imgutils import PartitionMount, mksparse, mkext4img, loop_detach
|
||||
from pylorax.imgutils import get_loop_name, dm_detach, mount, umount, Mount
|
||||
from pylorax.imgutils import mksquashfs, mkqemu_img, mktar, mkrootfsimg
|
||||
from pylorax.imgutils import copytree
|
||||
from pylorax.imgutils import copytree, mkcpio
|
||||
from pylorax.executils import execWithRedirect, execReadlines, runcmd
|
||||
from pylorax.monitor import LogMonitor
|
||||
from pylorax.mount import IsoMountpoint
|
||||
|
||||
# no-virt mode doesn't need libvirt, so make it optional
|
||||
try:
|
||||
import libvirt
|
||||
except ImportError:
|
||||
libvirt = None
|
||||
|
||||
# Default parameters for rebuilding initramfs, override with --dracut-args
|
||||
DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom qemu qemu-net",
|
||||
"--omit", "plymouth", "--no-hostonly", "--debug", "--no-early-microcode"]
|
||||
@ -92,11 +85,76 @@ class FakeDNF(object):
|
||||
pass
|
||||
|
||||
|
||||
class VirtualInstall(object):
|
||||
def find_free_port(start=5900, end=5999, host="127.0.0.1"):
|
||||
""" Return first free port in range.
|
||||
|
||||
:param int start: Starting port number
|
||||
:param int end: Ending port number
|
||||
:param str host: Host IP to search
|
||||
:returns: First free port or -1 if none found
|
||||
:rtype: int
|
||||
"""
|
||||
Run virt-install using an iso and a kickstart
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
for port in range(start, end+1):
|
||||
try:
|
||||
s.bind((host, port))
|
||||
s.close()
|
||||
return port
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
def append_initrd(initrd, files):
|
||||
""" Append files to an initrd.
|
||||
|
||||
:param str initrd: Path to initrd
|
||||
:param list files: list of file paths to add
|
||||
:returns: Path to a new initrd
|
||||
:rtype: str
|
||||
|
||||
The files are added to the initrd by creating a cpio image
|
||||
of the files (stored at /) and writing the cpio to the end of a
|
||||
copy of the initrd.
|
||||
|
||||
The initrd is not changed, a copy is made before appending the
|
||||
cpio archive.
|
||||
"""
|
||||
def __init__(self, iso, ks_paths, disk_img, img_size=2048,
|
||||
qemu_initrd = tempfile.mktemp(prefix="lmc-initrd-", suffix=".img")
|
||||
shutil.copy2(initrd, qemu_initrd)
|
||||
ks_dir = tempfile.mkdtemp(prefix="lmc-ksdir-")
|
||||
for ks in files:
|
||||
shutil.copy2(ks, ks_dir)
|
||||
ks_initrd = tempfile.mktemp(prefix="lmc-ks-", suffix=".img")
|
||||
mkcpio(ks_dir, ks_initrd)
|
||||
shutil.rmtree(ks_dir)
|
||||
with open(qemu_initrd, "ab") as initrd_fp:
|
||||
with open(ks_initrd, "rb") as ks_fp:
|
||||
while True:
|
||||
data = ks_fp.read(1024**2)
|
||||
if not data:
|
||||
break
|
||||
initrd_fp.write(data)
|
||||
os.unlink(ks_initrd)
|
||||
|
||||
return qemu_initrd
|
||||
|
||||
|
||||
class QEMUInstall(object):
|
||||
"""
|
||||
Run qemu using an iso and a kickstart
|
||||
"""
|
||||
# Mapping of arch to qemu command
|
||||
QEMU_CMDS = {"x86_64": "qemu-system-x86_64",
|
||||
"i386": "qemu-system-i386",
|
||||
"arm": "qemu-system-arm",
|
||||
"aarch64": "qemu-system-aarch64",
|
||||
"ppc": "qemu-system-ppc",
|
||||
"ppc64": "qemu-system-ppc64"
|
||||
}
|
||||
|
||||
def __init__(self, opts, iso, ks_paths, disk_img, img_size=2048,
|
||||
kernel_args=None, memory=1024, vcpus=None, vnc=None, arch=None,
|
||||
log_check=None, virtio_host="127.0.0.1", virtio_port=6080,
|
||||
image_type=None, boot_uefi=False, ovmf_path=None):
|
||||
@ -112,7 +170,7 @@ class VirtualInstall(object):
|
||||
:param str kernel_args: Extra kernel arguments to pass on the kernel cmdline
|
||||
:param int memory: Amount of RAM to assign to the virt, in MiB
|
||||
:param int vcpus: Number of virtual cpus
|
||||
:param str vnc: Arguments to pass to virt-install --graphics
|
||||
:param str vnc: Arguments to pass to qemu -display
|
||||
:param str arch: Optional architecture to use in the virt
|
||||
:param log_check: Method that returns True if the installation fails
|
||||
:type log_check: method
|
||||
@ -122,102 +180,97 @@ class VirtualInstall(object):
|
||||
:param bool boot_uefi: Use OVMF to boot the VM in UEFI mode
|
||||
:param str ovmf_path: Path to the OVMF firmware
|
||||
"""
|
||||
self.virt_name = "LiveOS-"+str(uuid.uuid4())
|
||||
# add --graphics none later
|
||||
# add whatever serial cmds are needed later
|
||||
args = ["-n", self.virt_name,
|
||||
"-r", str(memory),
|
||||
"--noreboot",
|
||||
"--noautoconsole"]
|
||||
# Lookup qemu-system- for arch if passed, or try to guess using host arch
|
||||
qemu_cmd = [self.QEMU_CMDS.get(arch or os.uname().machine, "qemu-system-"+os.uname().machine)]
|
||||
if not os.path.exists("/usr/bin/"+qemu_cmd[0]):
|
||||
raise InstallError("%s does not exist, cannot run qemu" % qemu_cmd[0])
|
||||
|
||||
args.append("--graphics")
|
||||
if vnc:
|
||||
args.append(vnc)
|
||||
else:
|
||||
args.append("none")
|
||||
qemu_cmd += ["-nodefconfig"]
|
||||
qemu_cmd += ["-m", str(memory)]
|
||||
if vcpus:
|
||||
qemu_cmd += ["-smp", str(vcpus)]
|
||||
|
||||
for ks in ks_paths:
|
||||
args.append("--initrd-inject")
|
||||
args.append(ks)
|
||||
if not opts.no_kvm and os.path.exists("/dev/kvm"):
|
||||
qemu_cmd += ["--machine", "accel=kvm"]
|
||||
|
||||
disk_opts = "path={0}".format(disk_img)
|
||||
disk_opts += ",cache=unsafe,discard=unmap"
|
||||
if image_type:
|
||||
disk_opts += ",format={0}".format(image_type)
|
||||
else:
|
||||
disk_opts += ",format=raw"
|
||||
# Copy the initrd from the iso, create a cpio archive of the kickstart files
|
||||
# and append it to the temporary initrd.
|
||||
qemu_initrd = append_initrd(iso.initrd, ks_paths)
|
||||
qemu_cmd += ["-kernel", iso.kernel]
|
||||
qemu_cmd += ["-initrd", qemu_initrd]
|
||||
|
||||
# Add the disk and cdrom
|
||||
if not os.path.isfile(disk_img):
|
||||
mksparse(disk_img, img_size * 1024**2)
|
||||
args.append("--disk")
|
||||
args.append(disk_opts)
|
||||
drive_args = "file=%s" % disk_img
|
||||
drive_args += ",cache=unsafe,discard=unmap"
|
||||
if image_type:
|
||||
drive_args += ",format=%s" % image_type
|
||||
else:
|
||||
drive_args += ",format=raw"
|
||||
qemu_cmd += ["-drive", drive_args]
|
||||
|
||||
if iso.stage2:
|
||||
disk_opts = "path={0},device=cdrom,readonly=on,shareable=on".format(iso.iso_path)
|
||||
args.append("--disk")
|
||||
args.append(disk_opts)
|
||||
drive_args = "file=%s,media=cdrom,readonly=on" % iso.iso_path
|
||||
qemu_cmd += ["-drive", drive_args]
|
||||
|
||||
extra_args = "ks=file:/{0}".format(os.path.basename(ks_paths[0]))
|
||||
if not vnc:
|
||||
extra_args += " inst.cmdline"
|
||||
# Setup the cmdline args
|
||||
# ======================
|
||||
cmdline_args = "ks=file:/%s" % os.path.basename(ks_paths[0])
|
||||
cmdline_args += " inst.stage2=hd:LABEL=%s" % udev_escape(iso.label)
|
||||
if opts.proxy:
|
||||
cmdline_args += " inst.proxy=%s" % opts.proxy
|
||||
if kernel_args:
|
||||
extra_args += " "+kernel_args
|
||||
if iso.stage2:
|
||||
extra_args += " stage2=hd:LABEL={0}".format(udev_escape(iso.label))
|
||||
args.append("--extra-args")
|
||||
args.append(extra_args)
|
||||
cmdline_args += " "+kernel_args
|
||||
cmdline_args += " inst.text inst.cmdline"
|
||||
|
||||
args.append("--location")
|
||||
args.append(iso.mount_dir)
|
||||
qemu_cmd += ["-append", cmdline_args]
|
||||
|
||||
channel_args = "tcp,host={0}:{1},mode=connect,target_type=virtio" \
|
||||
",name=org.fedoraproject.anaconda.log.0".format(
|
||||
virtio_host, virtio_port)
|
||||
args.append("--channel")
|
||||
args.append(channel_args)
|
||||
if not opts.vnc:
|
||||
vnc_port = find_free_port()
|
||||
if vnc_port == -1:
|
||||
raise InstallError("No free VNC ports")
|
||||
display_args = "vnc=127.0.0.1:%d" % (vnc_port - 5900)
|
||||
else:
|
||||
display_args = opts.vnc
|
||||
log.info("qemu %s", display_args)
|
||||
qemu_cmd += ["-nographic", "-display", display_args ]
|
||||
|
||||
if arch:
|
||||
args.append("--arch")
|
||||
args.append(arch)
|
||||
# Setup the virtio log port
|
||||
qemu_cmd += ["-device", "virtio-serial-pci,id=virtio-serial0"]
|
||||
qemu_cmd += ["-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0"
|
||||
",id=channel0,name=org.fedoraproject.anaconda.log.0"]
|
||||
qemu_cmd += ["-chardev", "socket,id=charchannel0,host=%s,port=%s" % (virtio_host, virtio_port)]
|
||||
|
||||
if vcpus:
|
||||
args.append("--vcpus")
|
||||
args.append(str(vcpus))
|
||||
# PAss through rng from host
|
||||
qemu_cmd += ["-object", "rng-random,id=virtio-rng0,filename=/dev/random"]
|
||||
qemu_cmd += ["-device", "virtio-rng-pci,rng=virtio-rng0,id=rng0,bus=pci.0,addr=0x9"]
|
||||
|
||||
if boot_uefi and ovmf_path:
|
||||
args.append("--boot")
|
||||
args.append(UEFI_FIRMWARE.format(ovmf_path))
|
||||
qemu_cmd += ["-drive", "file=%s/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on" % ovmf_path]
|
||||
qemu_cmd += ["-drive", "file=%sOVMF_VARS.fd,if=pflash,format=raw,unit=1" % ovmf_path]
|
||||
|
||||
log.info("Running virt-install.")
|
||||
log.info("Running qemu")
|
||||
log.debug(qemu_cmd)
|
||||
try:
|
||||
execWithRedirect("virt-install", args, raise_err=True)
|
||||
execWithRedirect(qemu_cmd[0], qemu_cmd[1:], reset_lang=False, raise_err=True,
|
||||
callback=lambda p: not log_check())
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise InstallError("Problem starting virtual install: %s" % e)
|
||||
|
||||
conn = libvirt.openReadOnly(None)
|
||||
dom = conn.lookupByName(self.virt_name)
|
||||
|
||||
# TODO: If vnc has been passed, we should look up the port and print that
|
||||
# for the user at this point
|
||||
while dom.isActive() and not log_check():
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
sleep(10)
|
||||
print()
|
||||
log.error("Running qemu failed:")
|
||||
log.error("cmd: %s", " ".join(e.cmd))
|
||||
log.error("output: %s", e.output or "")
|
||||
raise InstallError("QEMUInstall failed")
|
||||
except (OSError, KeyboardInterrupt) as e:
|
||||
log.error("Running qemu failed: %s", str(e))
|
||||
raise InstallError("QEMUInstall failed")
|
||||
finally:
|
||||
os.unlink(qemu_initrd)
|
||||
|
||||
if log_check():
|
||||
log.info("Installation error detected. See logfile.")
|
||||
log.error("Installation error detected. See logfile for details.")
|
||||
raise InstallError("QEMUInstall failed")
|
||||
else:
|
||||
log.info("Install finished. Or at least virt shut down.")
|
||||
log.info("Installation finished without errors.")
|
||||
|
||||
def destroy(self):
|
||||
"""
|
||||
Make sure the virt has been shut down and destroyed
|
||||
|
||||
Could use libvirt for this instead.
|
||||
"""
|
||||
log.info("Shutting down %s", self.virt_name)
|
||||
subprocess.call(["virsh", "destroy", self.virt_name])
|
||||
subprocess.call(["virsh", "undefine", self.virt_name])
|
||||
|
||||
def is_image_mounted(disk_img):
|
||||
"""
|
||||
@ -775,18 +828,22 @@ def novirt_install(opts, disk_img, disk_size):
|
||||
|
||||
def virt_install(opts, install_log, disk_img, disk_size):
|
||||
"""
|
||||
Use virt-install to install to a disk image
|
||||
Use qemu to install to a disk image
|
||||
|
||||
:param opts: options passed to livemedia-creator
|
||||
:type opts: argparse options
|
||||
:param str install_log: The path to write the log from virt-install
|
||||
:param str install_log: The path to write the log from qemu
|
||||
:param str disk_img: The full path to the disk image to be created
|
||||
:param int disk_size: The size of the disk_img in MiB
|
||||
|
||||
This uses virt-install 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.
|
||||
"""
|
||||
iso_mount = IsoMountpoint(opts.iso, opts.location)
|
||||
if not iso_mount.stage2:
|
||||
iso_mount.umount()
|
||||
raise InstallError("ISO is missing stage2, cannot continue")
|
||||
|
||||
log_monitor = LogMonitor(install_log, timeout=opts.timeout)
|
||||
|
||||
kernel_args = ""
|
||||
@ -796,7 +853,6 @@ def virt_install(opts, install_log, disk_img, disk_size):
|
||||
kernel_args += " proxy="+opts.proxy
|
||||
|
||||
if opts.image_type and not opts.make_fsimage:
|
||||
# virt-install can't take all the qcow2 options so create the image first
|
||||
qemu_args = []
|
||||
for arg in opts.qemu_args:
|
||||
qemu_args += arg.split(" ", 1)
|
||||
@ -811,15 +867,13 @@ def virt_install(opts, install_log, disk_img, disk_size):
|
||||
diskimg_path = disk_img
|
||||
|
||||
try:
|
||||
virt = VirtualInstall(iso_mount, opts.ks, diskimg_path, disk_size,
|
||||
kernel_args, opts.ram, opts.vcpus, opts.vnc, opts.arch,
|
||||
log_check = log_monitor.server.log_check,
|
||||
virtio_host = log_monitor.host,
|
||||
virtio_port = log_monitor.port,
|
||||
image_type=opts.image_type, boot_uefi=opts.virt_uefi,
|
||||
ovmf_path=opts.ovmf_path)
|
||||
|
||||
virt.destroy()
|
||||
QEMUInstall(opts, iso_mount, opts.ks, diskimg_path, disk_size,
|
||||
kernel_args, opts.ram, opts.vcpus, opts.vnc, opts.arch,
|
||||
log_check = log_monitor.server.log_check,
|
||||
virtio_host = log_monitor.host,
|
||||
virtio_port = log_monitor.port,
|
||||
image_type=opts.image_type, boot_uefi=opts.virt_uefi,
|
||||
ovmf_path=opts.ovmf_path)
|
||||
log_monitor.shutdown()
|
||||
except InstallError as e:
|
||||
log.error("VirtualInstall failed: %s", e)
|
||||
@ -931,7 +985,7 @@ def make_image(opts, ks):
|
||||
:returns: Path of the image created
|
||||
:rtype: str
|
||||
|
||||
Use virt-install or anaconda to install to a disk image.
|
||||
Use qemu+boot.iso or anaconda to install to a disk image.
|
||||
"""
|
||||
# Disk size for a filesystem image should only be the size of /
|
||||
# to prevent surprises when using the same kickstart for different installations.
|
||||
@ -1049,7 +1103,7 @@ def main():
|
||||
help="Build a Vagrant Box image")
|
||||
|
||||
parser.add_argument("--iso", type=os.path.abspath,
|
||||
help="Anaconda installation .iso path to use for virt-install")
|
||||
help="Anaconda installation .iso path to use for qemu")
|
||||
parser.add_argument("--iso-only", action="store_true",
|
||||
help="Remove all iso creation artifacts except the boot.iso, "
|
||||
"combine with --iso-name to rename the boot.iso")
|
||||
@ -1061,7 +1115,7 @@ def main():
|
||||
help="Exit after creating fs/disk image.")
|
||||
|
||||
parser.add_argument("--no-virt", action="store_true",
|
||||
help="Use Anaconda's image install instead of virt-install")
|
||||
help="Run anaconda directly on host instead of using qemu")
|
||||
parser.add_argument("--proxy",
|
||||
help="proxy URL to use for the install")
|
||||
parser.add_argument("--anaconda-arg", action="append", dest="anaconda_args",
|
||||
@ -1072,8 +1126,8 @@ def main():
|
||||
"i.e., highbank, mvebu, omap, tegra, etc.")
|
||||
parser.add_argument("--location", default=None, type=os.path.abspath,
|
||||
help="location of iso directory tree with initrd.img "
|
||||
"and vmlinuz. Used to run virt-install with a "
|
||||
"newer initrd than the iso.")
|
||||
"and vmlinuz. Used to run qemu with a newer initrd "
|
||||
"than the iso.")
|
||||
|
||||
parser.add_argument("--logfile", default="./livemedia.log",
|
||||
type=os.path.abspath,
|
||||
@ -1126,25 +1180,26 @@ def main():
|
||||
app_group.add_argument("--app-file", default="appliance.xml",
|
||||
help="Appliance template results file.")
|
||||
|
||||
# Group of arguments to pass to virt-install
|
||||
if not libvirt:
|
||||
virt_group = parser.add_argument_group("virt-install arguments (DISABLED -- no libvirt)")
|
||||
else:
|
||||
virt_group = parser.add_argument_group("virt-install arguments")
|
||||
# Group of arguments to pass to qemu
|
||||
virt_group = parser.add_argument_group("qemu arguments")
|
||||
virt_group.add_argument("--ram", metavar="MEMORY", type=int, default=1024,
|
||||
help="Memory to allocate for installer in megabytes.")
|
||||
virt_group.add_argument("--vcpus", type=int, default=None,
|
||||
help="Passed to --vcpus command")
|
||||
help="Passed to qemu -smp command")
|
||||
virt_group.add_argument("--vnc",
|
||||
help="Passed to --graphics command")
|
||||
help="Passed to qemu -display command. eg. vnc=127.0.0.1:5, default is to "
|
||||
"choose the first unused vnc port.")
|
||||
virt_group.add_argument("--arch", default=None,
|
||||
help="Passed to --arch command")
|
||||
help="System arch to build for. Used to select qemu-system-* command. "
|
||||
"Defaults to qemu-system-<arch>")
|
||||
virt_group.add_argument("--kernel-args",
|
||||
help="Additional argument to pass to the installation kernel")
|
||||
virt_group.add_argument("--ovmf-path", default="/usr/share/OVMF/",
|
||||
help="Path to OVMF firmware")
|
||||
virt_group.add_argument("--virt-uefi", action="store_true", default=False,
|
||||
help="Use OVMF firmware to boot the VM in UEFI mode")
|
||||
virt_group.add_argument("--no-kvm", action="store_true", default=False,
|
||||
help="Skip using kvm with qemu even if it is available.")
|
||||
|
||||
# dracut arguments
|
||||
dracut_group = parser.add_argument_group("dracut arguments")
|
||||
@ -1232,12 +1287,9 @@ def main():
|
||||
if opts.volid and len(opts.volid) > 32:
|
||||
errors.append("the volume id cannot be longer than 32 characters")
|
||||
|
||||
if is_install and not opts.no_virt and not libvirt:
|
||||
errors.append("virt install requires libvirt-python3 to be installed.")
|
||||
|
||||
if is_install and not opts.no_virt \
|
||||
and not os.path.exists("/usr/bin/virt-install"):
|
||||
errors.append("virt-install needs to be installed.")
|
||||
and not any(glob.glob("/usr/bin/qemu-system-*")):
|
||||
errors.append("qemu needs to be installed.")
|
||||
|
||||
if is_install and opts.no_virt \
|
||||
and not os.path.exists("/usr/sbin/anaconda"):
|
||||
|
Loading…
Reference in New Issue
Block a user