243 lines
8.3 KiB
Python
243 lines
8.3 KiB
Python
|
#
|
||
|
# 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.")
|