Mini Shell
#
# Copyright (C) 2019 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
import os
import shutil
import traceback
from glob import glob
from pyanaconda.core.util import execWithRedirect
from pyanaconda.modules.common.errors.payload import SourceSetupError, SourceTearDownError
from pyanaconda.modules.common.task import Task
from pyanaconda.modules.payloads.base.utils import create_root_dir, write_module_blacklist
from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)
class PrepareSystemForInstallationTask(Task):
"""Prepare system for the installation process.
Steps to prepare the installation root:
* Create a root directory
* Create a module blacklist from the boot cmdline
"""
def __init__(self, sysroot):
"""Create prepare system for installation task.
:param sysroot: path to the installation root
:type sysroot: str
"""
super().__init__()
self._sysroot = sysroot
@property
def name(self):
return "Prepare System for Installation"
def run(self):
"""Create a root and write module blacklist."""
create_root_dir(self._sysroot)
write_module_blacklist(self._sysroot)
class UpdateBLSConfigurationTask(Task):
"""Task to update BLS configuration."""
def __init__(self, sysroot, kernel_version_list):
"""Create a new task.
:param sysroot: a path to the root of the installed system
:type sysroot: str
:param kernel_version_list: list of kernel versions for updating of BLS configuration
:type kernel_version_list: list(str)
"""
super().__init__()
self._sysroot = sysroot
self._kernel_version_list = kernel_version_list
@property
def name(self):
return "Update BLS configuration."""
def run(self):
"""Run update of bls configuration."""
# Not using BLS configuration, skip it
if os.path.exists(self._sysroot + "/usr/sbin/new-kernel-pkg"):
return
# TODO: test if this is not a dir install
# Remove any existing BLS entries, they will not match the new system's
# machine-id or /boot mountpoint.
for file in glob(self._sysroot + "/boot/loader/entries/*.conf"):
log.info("Removing old BLS entry: %s", file)
os.unlink(file)
# Create new BLS entries for this system
for kernel in self._kernel_version_list:
log.info("Regenerating BLS info for %s", kernel)
execWithRedirect(
"kernel-install",
["add", kernel, "/lib/modules/{0}/vmlinuz".format(kernel)],
root=self._sysroot
)
# TODO: Can we remove this really old code which probably even doesn't work now?
# The tmpfs is mounted above Dracut /tmp where the below files are created so we never copy
# anything because we don't see those files.
# This code was introduced by commit 13f58e367f918320ce7f5be2e08c6a02ff90a087 with no bz number.
# It looks that the functionality is not required anymore on the newer implementation.
class CopyDriverDisksFilesTask(Task):
"""Copy driver disks files after installation to the installed system."""
DD_DIR = "/tmp/DD"
DD_FIRMWARE_DIR = "/tmp/DD/lib/firmware"
DD_RPMS_DIR = "/tmp"
DD_RPMS_GLOB = "DD-*"
def __init__(self, sysroot):
"""Create copy driver disks files task.
:param sysroot: path to the installation root
:type sysroot: str
"""
super().__init__()
self._sysroot = sysroot
@property
def name(self):
return "Copy Driver Disks Files"
def run(self):
"""Copy files from the driver disks to the installed system."""
# Multiple driver disks may be loaded, so we need to glob for all
# the firmware files in the common DD firmware directory
for f in glob(self.DD_FIRMWARE_DIR + "/*"):
try:
shutil.copyfile(f, os.path.join(self._sysroot, "lib/firmware/"))
except IOError as e:
log.error("Could not copy firmware file %s: %s", f, e.strerror)
# copy RPMS
for d in glob(os.path.join(self.DD_RPMS_DIR, self.DD_RPMS_GLOB)):
dest_dir = os.path.join(self._sysroot, "root/", os.path.basename(d))
shutil.copytree(d, dest_dir)
# copy modules and firmware into root's home directory
if os.path.exists(self.DD_DIR):
try:
shutil.copytree(self.DD_DIR, os.path.join(self._sysroot, "root/DD"))
except IOError as e:
log.error("failed to copy driver disk files: %s", e.strerror)
# XXX TODO: real error handling, as this is probably going to
# prevent boot on some systems
class SetUpSourcesTask(Task):
"""Set up all the installation source of the payload."""
def __init__(self, sources):
"""Create set up sources task.
The task will group all the sources set up tasks under this one.
:param sources: list of sources
:type sources: [instance of PayloadSourceBase class]
"""
super().__init__()
self._sources = sources
@property
def name(self):
return "Set Up Installation Sources"
def run(self):
"""Collect and call set up tasks for all the sources."""
if not self._sources:
raise SourceSetupError("No sources specified for set up!")
for source in self._sources:
tasks = source.set_up_with_tasks()
log.debug("Collected %s tasks from %s source",
[task.name for task in tasks],
source.type)
for task in tasks:
log.debug("Running task %s", task.name)
task.run_with_signals()
class TearDownSourcesTask(Task):
"""Tear down all the installation sources of the payload."""
def __init__(self, sources):
"""Create tear down sources task.
The task will group all the sources tear down tasks under this one.
:param sources: list of sources
:type sources: [instance of PayloadSourceBase class]
"""
super().__init__()
self._sources = sources
@property
def name(self):
return "Tear Down Installation Sources"
def run(self):
"""Collect and call tear down tasks for all the sources."""
if not self._sources:
raise SourceSetupError("No sources specified for tear down!")
errors = []
for source in self._sources:
tasks = source.tear_down_with_tasks()
log.debug("Collected %s tasks from %s source",
[task.name for task in tasks],
source.type)
for task in tasks:
log.debug("Running task %s", task.name)
try:
task.run()
except SourceTearDownError as e:
message = "Task '{}' from source '{}' has failed, reason: {}".format(
task.name, source.type, str(e))
errors.append(message)
log.error("%s\n%s", message, traceback.format_exc())
if errors:
raise SourceTearDownError("Sources tear down have failed", errors)