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
|
.TP
|
||||||
\fB\-\-vnc VNC\fR
|
\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
|
.TP
|
||||||
\fB\-\-arch ARCH\fR
|
\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
|
use with virtualization and container solutions like libvirt, docker, and
|
||||||
OpenStack.
|
OpenStack.
|
||||||
|
|
||||||
The general idea is to use virt-install with kickstart and an Anaconda boot.iso
|
The general idea is to use qemu with kickstart and an Anaconda boot.iso to
|
||||||
to install into a disk image and then use the disk image to create the bootable
|
install into a disk image and then use the disk image to create the bootable
|
||||||
media.
|
media.
|
||||||
|
|
||||||
livemedia-creator --help will describe all of the options available. At the
|
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.
|
``--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.
|
``--ks`` to select the kickstart file describing what to install.
|
||||||
|
|
||||||
To use livemedia-creator with virt-install you will need to install the
|
To use livemedia-creator with virtualization you will need to have qemu installed.
|
||||||
following packages, as well as have libvirtd setup correctly.
|
|
||||||
|
|
||||||
* ``virt-install``
|
|
||||||
* ``libvirt-python``
|
|
||||||
|
|
||||||
If you are going to be using Anaconda directly, with ``--no-virt`` mode, make sure
|
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
|
you have the anaconda-tui package installed.
|
||||||
to save a bit of space on the build system.
|
|
||||||
|
|
||||||
Conventions used in this document:
|
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 \
|
--make-iso --iso=/extra/iso/boot.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 client
|
You can observe the installation using vnc. The logs will show what port was
|
||||||
to connect to localhost:0
|
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
|
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.
|
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
|
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``
|
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
|
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
|
kickstart is used to customize the installed system in the same way that
|
||||||
current spin-kickstarts do.
|
current spin-kickstarts do.
|
||||||
@ -82,24 +77,22 @@ 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
|
||||||
directory specified by the --logfile command. You can also monitor the install
|
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
|
by using a vnc client. This is recommended when first modifying a kickstart,
|
||||||
modifying a kickstart, since there are still places where Anaconda may get
|
since there are still places where Anaconda may get stuck without the log
|
||||||
stuck without the log monitor catching it.
|
monitor catching it.
|
||||||
|
|
||||||
The output from this process is a partitioned disk image. kpartx can be used
|
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
|
to mount and examine it when there is a problem with the install. It can also
|
||||||
be booted using kvm.
|
be booted using kvm.
|
||||||
|
|
||||||
When creating an iso the disk image's / partition is copied into a formatted
|
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
|
filesystem image which is then used as the input to lorax for creation of the
|
||||||
media.
|
final media.
|
||||||
|
|
||||||
The final image is created by lorax, using the templates in /usr/share/lorax/
|
The final image is created by lorax, using the templates in /usr/share/lorax/live/
|
||||||
or the directory specified by ``--lorax-templates``
|
or the live directory below the directory specified by ``--lorax-templates``. The
|
||||||
|
templates are written using the Mako template system with some extra commands
|
||||||
Currently the standard lorax templates are used to make a bootable iso, but
|
added by lorax.
|
||||||
it should be possible to modify them to output other results. They are
|
|
||||||
written using the Mako template system which is very flexible.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The output from --make-iso includes the artifacts used to create the boot.iso;
|
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+
|
memtest86+
|
||||||
syslinux
|
syslinux
|
||||||
|
|
||||||
One drawback to using virt-install is that it pulls the packages from
|
One drawback to using qemu is that it pulls the packages from the repo each
|
||||||
the repo each time you run it. To speed things up you either need a local
|
time you run it. To speed things up you either need a local mirror of the
|
||||||
mirror of the packages, or you can use a caching proxy. When using a proxy
|
packages, or you can use a caching proxy. When using a proxy you pass it to
|
||||||
you pass it to livemedia-creator like this:
|
livemedia-creator like this:
|
||||||
|
|
||||||
``--proxy=http://proxy.yourdomain.com:3128``
|
``--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/"``
|
``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
|
You can also add an update repo, but don't name it updates. Add --proxy to it
|
||||||
it as well.
|
as well.
|
||||||
|
|
||||||
|
|
||||||
Anaconda image install (no-virt)
|
Anaconda image install (no-virt)
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
You can create images without using virt-install by passing ``--no-virt`` on the
|
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.
|
cmdline. This will use Anaconda's directory install feature to handle the
|
||||||
There are a couple of things to keep in mind when doing this:
|
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
|
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
|
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
|
Filesystem Image Creation
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
livemedia-creator can be used to create un-partitined filesystem images using the
|
livemedia-creator can be used to create un-partitined filesystem images using
|
||||||
``--make-fsimage`` option. As of version 21.8 this works with both virt-install and no-virt modes
|
the ``--make-fsimage`` option. As of version 21.8 this works with both qemu and
|
||||||
of operation. Previously it was only available with no-virt.
|
no-virt modes of operation. Previously it was only available with no-virt.
|
||||||
|
|
||||||
Kickstarts should have a single / partition with no extra mountpoints.
|
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.
|
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
|
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.
|
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.
|
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
|
OpenStack Image Creation
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
@ -528,29 +581,27 @@ Make sure that the kickstart you are using creates a /boot/efi partition by incl
|
|||||||
Debugging problems
|
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
|
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
|
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.
|
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
|
When creating a new kickstart it is helpful to use vnc so that you can monitor
|
||||||
that you can monitor the installation as it happens, and if it gets stuck
|
the installation as it happens, and if it gets stuck without lmc detecting the
|
||||||
without lmc detecting the problem you can switch to tty1 and examine the system
|
problem you can switch to tty1 and examine the system directly.
|
||||||
directly.
|
|
||||||
|
|
||||||
If it does get stuck the best way to cancel is to use virsh to destroy the domain.
|
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.
|
||||||
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 lmc didn't handle the cleanup for some reason you can do this:
|
If lmc didn't handle the cleanup for some reason you can do this:
|
||||||
1. ``sudo virsh undefine <name>``
|
1. ``sudo umount /tmp/lmc-XXXX`` to unmount the iso from its mountpoint.
|
||||||
2. ``sudo umount /tmp/tmpXXXX`` to unmount the iso from its mountpoint.
|
2. ``sudo rm -rf /tmp/lmc-XXXX``
|
||||||
3. ``sudo rm -rf /tmp/tmpXXXX``
|
3. ``sudo rm /var/tmp/lmc-disk-XXXXX`` to remove the disk image.
|
||||||
4. ``sudo rm /var/tmp/diskXXXXX`` to remove the disk image.
|
|
||||||
|
|
||||||
The logs from the virt-install run are stored in virt-install.log,
|
Note that lmc uses the lmc- prefix for all of its temporary files and
|
||||||
logs from livemedia-creator are in livemedia.log and program.log
|
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
|
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
|
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 os
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
from time import sleep
|
|
||||||
import shutil
|
import shutil
|
||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
import socket
|
||||||
|
|
||||||
# Use pykickstart to calculate disk image size
|
# Use pykickstart to calculate disk image size
|
||||||
from pykickstart.parser import KickstartParser
|
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 PartitionMount, mksparse, mkext4img, loop_detach
|
||||||
from pylorax.imgutils import get_loop_name, dm_detach, mount, umount, Mount
|
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 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.executils import execWithRedirect, execReadlines, runcmd
|
||||||
from pylorax.monitor import LogMonitor
|
from pylorax.monitor import LogMonitor
|
||||||
from pylorax.mount import IsoMountpoint
|
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
|
# Default parameters for rebuilding initramfs, override with --dracut-args
|
||||||
DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom qemu qemu-net",
|
DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom qemu qemu-net",
|
||||||
"--omit", "plymouth", "--no-hostonly", "--debug", "--no-early-microcode"]
|
"--omit", "plymouth", "--no-hostonly", "--debug", "--no-early-microcode"]
|
||||||
@ -92,11 +85,76 @@ class FakeDNF(object):
|
|||||||
pass
|
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,
|
kernel_args=None, memory=1024, vcpus=None, vnc=None, arch=None,
|
||||||
log_check=None, virtio_host="127.0.0.1", virtio_port=6080,
|
log_check=None, virtio_host="127.0.0.1", virtio_port=6080,
|
||||||
image_type=None, boot_uefi=False, ovmf_path=None):
|
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 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 memory: Amount of RAM to assign to the virt, in MiB
|
||||||
:param int vcpus: Number of virtual cpus
|
: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 str arch: Optional architecture to use in the virt
|
||||||
:param log_check: Method that returns True if the installation fails
|
:param log_check: Method that returns True if the installation fails
|
||||||
:type log_check: method
|
: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 bool boot_uefi: Use OVMF to boot the VM in UEFI mode
|
||||||
:param str ovmf_path: Path to the OVMF firmware
|
:param str ovmf_path: Path to the OVMF firmware
|
||||||
"""
|
"""
|
||||||
self.virt_name = "LiveOS-"+str(uuid.uuid4())
|
# Lookup qemu-system- for arch if passed, or try to guess using host arch
|
||||||
# add --graphics none later
|
qemu_cmd = [self.QEMU_CMDS.get(arch or os.uname().machine, "qemu-system-"+os.uname().machine)]
|
||||||
# add whatever serial cmds are needed later
|
if not os.path.exists("/usr/bin/"+qemu_cmd[0]):
|
||||||
args = ["-n", self.virt_name,
|
raise InstallError("%s does not exist, cannot run qemu" % qemu_cmd[0])
|
||||||
"-r", str(memory),
|
|
||||||
"--noreboot",
|
|
||||||
"--noautoconsole"]
|
|
||||||
|
|
||||||
args.append("--graphics")
|
qemu_cmd += ["-nodefconfig"]
|
||||||
if vnc:
|
qemu_cmd += ["-m", str(memory)]
|
||||||
args.append(vnc)
|
if vcpus:
|
||||||
else:
|
qemu_cmd += ["-smp", str(vcpus)]
|
||||||
args.append("none")
|
|
||||||
|
|
||||||
for ks in ks_paths:
|
if not opts.no_kvm and os.path.exists("/dev/kvm"):
|
||||||
args.append("--initrd-inject")
|
qemu_cmd += ["--machine", "accel=kvm"]
|
||||||
args.append(ks)
|
|
||||||
|
|
||||||
disk_opts = "path={0}".format(disk_img)
|
# Copy the initrd from the iso, create a cpio archive of the kickstart files
|
||||||
disk_opts += ",cache=unsafe,discard=unmap"
|
# and append it to the temporary initrd.
|
||||||
if image_type:
|
qemu_initrd = append_initrd(iso.initrd, ks_paths)
|
||||||
disk_opts += ",format={0}".format(image_type)
|
qemu_cmd += ["-kernel", iso.kernel]
|
||||||
else:
|
qemu_cmd += ["-initrd", qemu_initrd]
|
||||||
disk_opts += ",format=raw"
|
|
||||||
|
# Add the disk and cdrom
|
||||||
if not os.path.isfile(disk_img):
|
if not os.path.isfile(disk_img):
|
||||||
mksparse(disk_img, img_size * 1024**2)
|
mksparse(disk_img, img_size * 1024**2)
|
||||||
args.append("--disk")
|
drive_args = "file=%s" % disk_img
|
||||||
args.append(disk_opts)
|
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:
|
drive_args = "file=%s,media=cdrom,readonly=on" % iso.iso_path
|
||||||
disk_opts = "path={0},device=cdrom,readonly=on,shareable=on".format(iso.iso_path)
|
qemu_cmd += ["-drive", drive_args]
|
||||||
args.append("--disk")
|
|
||||||
args.append(disk_opts)
|
|
||||||
|
|
||||||
extra_args = "ks=file:/{0}".format(os.path.basename(ks_paths[0]))
|
# Setup the cmdline args
|
||||||
if not vnc:
|
# ======================
|
||||||
extra_args += " inst.cmdline"
|
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:
|
if kernel_args:
|
||||||
extra_args += " "+kernel_args
|
cmdline_args += " "+kernel_args
|
||||||
if iso.stage2:
|
cmdline_args += " inst.text inst.cmdline"
|
||||||
extra_args += " stage2=hd:LABEL={0}".format(udev_escape(iso.label))
|
|
||||||
args.append("--extra-args")
|
|
||||||
args.append(extra_args)
|
|
||||||
|
|
||||||
args.append("--location")
|
qemu_cmd += ["-append", cmdline_args]
|
||||||
args.append(iso.mount_dir)
|
|
||||||
|
|
||||||
channel_args = "tcp,host={0}:{1},mode=connect,target_type=virtio" \
|
if not opts.vnc:
|
||||||
",name=org.fedoraproject.anaconda.log.0".format(
|
vnc_port = find_free_port()
|
||||||
virtio_host, virtio_port)
|
if vnc_port == -1:
|
||||||
args.append("--channel")
|
raise InstallError("No free VNC ports")
|
||||||
args.append(channel_args)
|
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:
|
# Setup the virtio log port
|
||||||
args.append("--arch")
|
qemu_cmd += ["-device", "virtio-serial-pci,id=virtio-serial0"]
|
||||||
args.append(arch)
|
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:
|
# PAss through rng from host
|
||||||
args.append("--vcpus")
|
qemu_cmd += ["-object", "rng-random,id=virtio-rng0,filename=/dev/random"]
|
||||||
args.append(str(vcpus))
|
qemu_cmd += ["-device", "virtio-rng-pci,rng=virtio-rng0,id=rng0,bus=pci.0,addr=0x9"]
|
||||||
|
|
||||||
if boot_uefi and ovmf_path:
|
if boot_uefi and ovmf_path:
|
||||||
args.append("--boot")
|
qemu_cmd += ["-drive", "file=%s/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on" % ovmf_path]
|
||||||
args.append(UEFI_FIRMWARE.format(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:
|
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:
|
except subprocess.CalledProcessError as e:
|
||||||
raise InstallError("Problem starting virtual install: %s" % e)
|
log.error("Running qemu failed:")
|
||||||
|
log.error("cmd: %s", " ".join(e.cmd))
|
||||||
conn = libvirt.openReadOnly(None)
|
log.error("output: %s", e.output or "")
|
||||||
dom = conn.lookupByName(self.virt_name)
|
raise InstallError("QEMUInstall failed")
|
||||||
|
except (OSError, KeyboardInterrupt) as e:
|
||||||
# TODO: If vnc has been passed, we should look up the port and print that
|
log.error("Running qemu failed: %s", str(e))
|
||||||
# for the user at this point
|
raise InstallError("QEMUInstall failed")
|
||||||
while dom.isActive() and not log_check():
|
finally:
|
||||||
sys.stdout.write(".")
|
os.unlink(qemu_initrd)
|
||||||
sys.stdout.flush()
|
|
||||||
sleep(10)
|
|
||||||
print()
|
|
||||||
|
|
||||||
if log_check():
|
if log_check():
|
||||||
log.info("Installation error detected. See logfile.")
|
log.error("Installation error detected. See logfile for details.")
|
||||||
|
raise InstallError("QEMUInstall failed")
|
||||||
else:
|
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):
|
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):
|
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
|
:param opts: options passed to livemedia-creator
|
||||||
:type opts: argparse options
|
: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 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
|
: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.
|
image and then optionally, based on the opts passed, creates tarfile.
|
||||||
"""
|
"""
|
||||||
iso_mount = IsoMountpoint(opts.iso, opts.location)
|
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)
|
log_monitor = LogMonitor(install_log, timeout=opts.timeout)
|
||||||
|
|
||||||
kernel_args = ""
|
kernel_args = ""
|
||||||
@ -796,7 +853,6 @@ def virt_install(opts, install_log, disk_img, disk_size):
|
|||||||
kernel_args += " proxy="+opts.proxy
|
kernel_args += " proxy="+opts.proxy
|
||||||
|
|
||||||
if opts.image_type and not opts.make_fsimage:
|
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 = []
|
qemu_args = []
|
||||||
for arg in opts.qemu_args:
|
for arg in opts.qemu_args:
|
||||||
qemu_args += arg.split(" ", 1)
|
qemu_args += arg.split(" ", 1)
|
||||||
@ -811,15 +867,13 @@ def virt_install(opts, install_log, disk_img, disk_size):
|
|||||||
diskimg_path = disk_img
|
diskimg_path = disk_img
|
||||||
|
|
||||||
try:
|
try:
|
||||||
virt = VirtualInstall(iso_mount, opts.ks, diskimg_path, disk_size,
|
QEMUInstall(opts, iso_mount, opts.ks, diskimg_path, disk_size,
|
||||||
kernel_args, opts.ram, opts.vcpus, opts.vnc, opts.arch,
|
kernel_args, opts.ram, opts.vcpus, opts.vnc, opts.arch,
|
||||||
log_check = log_monitor.server.log_check,
|
log_check = log_monitor.server.log_check,
|
||||||
virtio_host = log_monitor.host,
|
virtio_host = log_monitor.host,
|
||||||
virtio_port = log_monitor.port,
|
virtio_port = log_monitor.port,
|
||||||
image_type=opts.image_type, boot_uefi=opts.virt_uefi,
|
image_type=opts.image_type, boot_uefi=opts.virt_uefi,
|
||||||
ovmf_path=opts.ovmf_path)
|
ovmf_path=opts.ovmf_path)
|
||||||
|
|
||||||
virt.destroy()
|
|
||||||
log_monitor.shutdown()
|
log_monitor.shutdown()
|
||||||
except InstallError as e:
|
except InstallError as e:
|
||||||
log.error("VirtualInstall failed: %s", e)
|
log.error("VirtualInstall failed: %s", e)
|
||||||
@ -931,7 +985,7 @@ def make_image(opts, ks):
|
|||||||
:returns: Path of the image created
|
:returns: Path of the image created
|
||||||
:rtype: str
|
: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 /
|
# Disk size for a filesystem image should only be the size of /
|
||||||
# to prevent surprises when using the same kickstart for different installations.
|
# to prevent surprises when using the same kickstart for different installations.
|
||||||
@ -1049,7 +1103,7 @@ def main():
|
|||||||
help="Build a Vagrant Box image")
|
help="Build a Vagrant Box image")
|
||||||
|
|
||||||
parser.add_argument("--iso", type=os.path.abspath,
|
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",
|
parser.add_argument("--iso-only", action="store_true",
|
||||||
help="Remove all iso creation artifacts except the boot.iso, "
|
help="Remove all iso creation artifacts except the boot.iso, "
|
||||||
"combine with --iso-name to rename 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.")
|
help="Exit after creating fs/disk image.")
|
||||||
|
|
||||||
parser.add_argument("--no-virt", action="store_true",
|
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",
|
parser.add_argument("--proxy",
|
||||||
help="proxy URL to use for the install")
|
help="proxy URL to use for the install")
|
||||||
parser.add_argument("--anaconda-arg", action="append", dest="anaconda_args",
|
parser.add_argument("--anaconda-arg", action="append", dest="anaconda_args",
|
||||||
@ -1072,8 +1126,8 @@ def main():
|
|||||||
"i.e., highbank, mvebu, omap, tegra, etc.")
|
"i.e., highbank, mvebu, omap, tegra, etc.")
|
||||||
parser.add_argument("--location", default=None, type=os.path.abspath,
|
parser.add_argument("--location", default=None, type=os.path.abspath,
|
||||||
help="location of iso directory tree with initrd.img "
|
help="location of iso directory tree with initrd.img "
|
||||||
"and vmlinuz. Used to run virt-install with a "
|
"and vmlinuz. Used to run qemu with a newer initrd "
|
||||||
"newer initrd than the iso.")
|
"than the iso.")
|
||||||
|
|
||||||
parser.add_argument("--logfile", default="./livemedia.log",
|
parser.add_argument("--logfile", default="./livemedia.log",
|
||||||
type=os.path.abspath,
|
type=os.path.abspath,
|
||||||
@ -1126,25 +1180,26 @@ def main():
|
|||||||
app_group.add_argument("--app-file", default="appliance.xml",
|
app_group.add_argument("--app-file", default="appliance.xml",
|
||||||
help="Appliance template results file.")
|
help="Appliance template results file.")
|
||||||
|
|
||||||
# Group of arguments to pass to virt-install
|
# Group of arguments to pass to qemu
|
||||||
if not libvirt:
|
virt_group = parser.add_argument_group("qemu arguments")
|
||||||
virt_group = parser.add_argument_group("virt-install arguments (DISABLED -- no libvirt)")
|
|
||||||
else:
|
|
||||||
virt_group = parser.add_argument_group("virt-install arguments")
|
|
||||||
virt_group.add_argument("--ram", metavar="MEMORY", type=int, default=1024,
|
virt_group.add_argument("--ram", metavar="MEMORY", type=int, default=1024,
|
||||||
help="Memory to allocate for installer in megabytes.")
|
help="Memory to allocate for installer in megabytes.")
|
||||||
virt_group.add_argument("--vcpus", type=int, default=None,
|
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",
|
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,
|
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",
|
virt_group.add_argument("--kernel-args",
|
||||||
help="Additional argument to pass to the installation kernel")
|
help="Additional argument to pass to the installation kernel")
|
||||||
virt_group.add_argument("--ovmf-path", default="/usr/share/OVMF/",
|
virt_group.add_argument("--ovmf-path", default="/usr/share/OVMF/",
|
||||||
help="Path to OVMF firmware")
|
help="Path to OVMF firmware")
|
||||||
virt_group.add_argument("--virt-uefi", action="store_true", default=False,
|
virt_group.add_argument("--virt-uefi", action="store_true", default=False,
|
||||||
help="Use OVMF firmware to boot the VM in UEFI mode")
|
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 arguments
|
||||||
dracut_group = parser.add_argument_group("dracut arguments")
|
dracut_group = parser.add_argument_group("dracut arguments")
|
||||||
@ -1232,12 +1287,9 @@ def main():
|
|||||||
if opts.volid and len(opts.volid) > 32:
|
if opts.volid and len(opts.volid) > 32:
|
||||||
errors.append("the volume id cannot be longer than 32 characters")
|
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 \
|
if is_install and not opts.no_virt \
|
||||||
and not os.path.exists("/usr/bin/virt-install"):
|
and not any(glob.glob("/usr/bin/qemu-system-*")):
|
||||||
errors.append("virt-install needs to be installed.")
|
errors.append("qemu needs to be installed.")
|
||||||
|
|
||||||
if is_install and opts.no_virt \
|
if is_install and opts.no_virt \
|
||||||
and not os.path.exists("/usr/sbin/anaconda"):
|
and not os.path.exists("/usr/sbin/anaconda"):
|
||||||
|
Loading…
Reference in New Issue
Block a user