anaconda/anaconda-40.22.3.13/pyanaconda/modules/boss/installation.py

243 lines
8.3 KiB
Python
Raw Normal View History

2024-11-14 21:39:56 -08:00
#
# Copyright (C) 2021 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.path
import shutil
import glob
from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.path import make_directories, join_paths, open_with_perm
from pyanaconda.core.util import execWithRedirect, restorecon
from pyanaconda.modules.common.task import Task
log = get_module_logger(__name__)
TARGET_LOG_DIR = "/var/log/anaconda/"
class CopyLogsTask(Task):
"""Task to copy logs to target system."""
def __init__(self, sysroot):
"""Create a new task.
:param sysroot: a path to the root of installed system
:type sysroot: str
"""
super().__init__()
self._sysroot = sysroot
@property
def name(self):
return "Copy installation logs"
def run(self):
"""Copy installation logs and other related files, and do necessary operations on them.
- Copy logs of all kinds, incl. ks scripts, journal dump, and lorax pkg list
- Copy input kickstart file
- Autorelabel everything in the destination
"""
self._copy_kickstart()
self._copy_logs()
def _copy_logs(self):
"""Copy installation logs to the target system"""
if not conf.target.can_save_installation_logs:
log.warning("Installation logs will not be saved to the installed system due to the "
"nosave option.")
return
log.info("Copying logs from the installation environment.")
self._create_logs_directory()
self._copy_tmp_logs()
self._copy_lorax_packages()
self._copy_pre_script_logs()
self._copy_dnf_debugdata()
self._copy_post_script_logs()
self._dump_journal()
self._relabel_log_files()
def _create_logs_directory(self):
"""Create directory for Anaconda logs on the install target"""
make_directories(join_paths(self._sysroot, TARGET_LOG_DIR))
def _copy_tmp_logs(self):
"""Copy a number of log files from /tmp"""
log_files_to_copy = [
"anaconda.log",
"syslog",
"X.log",
"program.log",
"packaging.log",
"storage.log",
"ifcfg.log",
"lvm.log",
"dnf.librepo.log",
"hawkey.log",
"dbus.log",
]
for logfile in log_files_to_copy:
self._copy_file_to_sysroot(
join_paths("/tmp/", logfile),
join_paths(TARGET_LOG_DIR, logfile)
)
def _copy_lorax_packages(self):
"""Copy list of packages used for creating the installation media"""
self._copy_file_to_sysroot(
"/root/lorax-packages.log",
join_paths(TARGET_LOG_DIR, "lorax-packages.log")
)
def _copy_pre_script_logs(self):
"""Copy logs from %pre scripts"""
self._copy_tree_to_sysroot(
"/tmp/pre-anaconda-logs",
TARGET_LOG_DIR
)
def _copy_dnf_debugdata(self):
"""Copy DNF debug data"""
self._copy_tree_to_sysroot(
"/root/debugdata",
join_paths(TARGET_LOG_DIR, "dnf_debugdata/")
)
def _copy_post_script_logs(self):
"""Copy logs from %post scripts"""
for logfile in glob.glob("/tmp/ks-script*.log"):
self._copy_file_to_sysroot(
logfile,
join_paths(TARGET_LOG_DIR, os.path.basename(logfile))
)
def _dump_journal(self):
"""Dump journal from the installation environment"""
tempfile = "/tmp/journal.log"
with open_with_perm(tempfile, "w", perm=0o600) as logfile:
execWithRedirect("journalctl", ["-b"], stdout=logfile, log_output=False)
self._copy_file_to_sysroot(tempfile, join_paths(TARGET_LOG_DIR, "journal.log"))
def _copy_kickstart(self):
"""Copy input kickstart file"""
if conf.target.can_copy_input_kickstart:
log.info("Copying input kickstart file.")
self._copy_file_to_sysroot(
"/run/install/ks.cfg",
"/root/original-ks.cfg"
)
else:
log.warning("Input kickstart will not be saved to the installed system due to the "
"nosave option.")
def _relabel_log_files(self):
"""Relabel the anaconda logs.
The files we've just copied could be incorrectly labeled, like hawkey.log:
https://bugzilla.redhat.com/show_bug.cgi?id=1885772
"""
if not restorecon([TARGET_LOG_DIR], root=self._sysroot, skip_nonexistent=True):
log.error("Log file contexts were not restored because restorecon was not installed.")
def _copy_file_to_sysroot(self, src, dest):
"""Copy a file, if it exists, and set its access bits.
:param str src: path to source file
:param str dest: path to destination file within sysroot
"""
if os.path.exists(src):
log.info("Copying file: %s -> %s", src, dest)
full_dest_path = join_paths(self._sysroot, dest)
shutil.copyfile(
src,
full_dest_path
)
os.chmod(full_dest_path, 0o0600)
def _copy_tree_to_sysroot(self, src, dest):
"""Copy a directory tree, if it exists, and set its access bits.
:param str src: path to source directory
:param str dest: path to destination directory within sysroot
"""
if os.path.exists(src):
log.info("Copying directory tree: %s -> %s", src, dest)
full_dest_path = join_paths(self._sysroot, dest)
shutil.copytree(
src,
full_dest_path,
dirs_exist_ok=True
)
os.chmod(full_dest_path, 0o0600)
class SetContextsTask(Task):
"""Task to set file contexts on target system.
We need to handle SELinux relabeling for a few reasons:
- %post scripts that write files into places in /etc, but don't do labeling correctly
- Anaconda code that does the same (e.g. moving our log files into /var/log/anaconda)
- ostree payloads, where all of the labeling of /var is the installer's responsibility
(see https://github.com/ostreedev/ostree/pull/872 )
- OSTree variants of the traditional mounts if present
"""
def __init__(self, sysroot):
"""Create a new task.
:param sysroot: a path to the root of installed system
:type sysroot: str
"""
super().__init__()
self._sysroot = sysroot
@property
def name(self):
return "Set file contexts"
def run(self):
"""Relabel files (set contexts).
Do not fail if the executable is not present.
"""
dirs_to_relabel = [
"/boot",
"/dev",
"/etc",
"/lib64",
"/root",
"/usr/lib",
"/usr/lib64",
"/var/cache/yum",
"/var/home",
"/var/lib",
"/var/lock",
"/var/log",
"/var/media",
"/var/mnt",
"/var/opt",
"/var/roothome",
"/var/run",
"/var/spool",
"/var/srv"
]
log.info("Restoring SELinux contexts.")
if not restorecon(dirs_to_relabel, root=self._sysroot, skip_nonexistent=True):
log.warning("Cannot restore contexts because restorecon was not installed.")