anaconda/anaconda-40.22.3.13/pyanaconda/modules/subscription/installation.py
2024-11-14 21:39:56 -08:00

247 lines
10 KiB
Python

#
# Copyright (C) 2020 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 glob
import shutil
from dasbus.typing import get_variant, Str
from pyanaconda.core import util
from pyanaconda.core.constants import RHSM_SYSPURPOSE_FILE_PATH
from pyanaconda.core.path import make_directories, join_paths
from pyanaconda.core.subscription import check_system_purpose_set
from pyanaconda.modules.common.task import Task
from pyanaconda.modules.common.errors.installation import InsightsConnectError, \
InsightsClientMissingError, SubscriptionTokenTransferError
from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)
class ConnectToInsightsTask(Task):
"""Connect the target system to Red Hat Insights."""
INSIGHTS_TOOL_PATH = "/usr/bin/insights-client"
def __init__(self, sysroot, subscription_attached, connect_to_insights):
"""Create a new task.
:param str sysroot: target system root path
:param bool subscription_attached: if True then the system has been subscribed,
False otherwise
:param bool connect_to_insights: if True then connect the system to Insights,
if False do nothing
"""
super().__init__()
self._sysroot = sysroot
self._subscription_attached = subscription_attached
self._connect_to_insights = connect_to_insights
@property
def name(self):
return "Connect the target system to Red Hat Insights"
def run(self):
"""Connect the target system to Red Hat Insights."""
# check if we should connect to Red Hat Insights
if not self._connect_to_insights:
log.debug("insights-connect-task: Insights not requested, skipping")
return
elif not self._subscription_attached:
log.debug("insights-connect-task: "
"Insights requested but target system is not subscribed, skipping")
return
insights_path = join_paths(self._sysroot, self.INSIGHTS_TOOL_PATH)
# check the insights client utility is available
if not os.path.isfile(insights_path):
raise InsightsClientMissingError(
"The insight-client tool ({}) is not available.".format(self.INSIGHTS_TOOL_PATH)
)
# tell the insights client to connect to insights
log.debug("insights-connect-task: connecting to insights")
rc = util.execWithRedirect(self.INSIGHTS_TOOL_PATH, ["--register"], root=self._sysroot)
if rc:
raise InsightsConnectError("Failed to connect to Red Hat Insights.")
class RestoreRHSMDefaultsTask(Task):
"""Restore RHSM defaults we changed for install time purposes.
At the moment this means setting the RHSM log level back to INFO
from DEBUG and making sure SSL certificate validation is enabled
(as we might turn it off for the installation run if requested by
the user).
"""
def __init__(self, rhsm_config_proxy):
"""Create a new task.
:param rhsm_config_proxy: DBus proxy for the RHSM Config object
"""
super().__init__()
self._rhsm_config_proxy = rhsm_config_proxy
@property
def name(self):
return "Restoring subscription manager defaults"
def run(self):
"""Restore RHSM defaults we changed.
We previously set the RHSM log level to DEBUG, which is also
reflected in rhsm.conf. This would mean RHSM would continue to
log in debug mode also on the system once rhsm.conf has been
copied over to the target system.
The same thing needs to be done for the server.insecure key
that we migh set to "1" previously on user request.
So set the log level back to INFO before we copy the config file
and make sure server.insecure is equal to "0".
"""
log.debug("subscription: setting RHSM log level back to INFO")
log.debug("subscription: making sure RHSM SSL certificate validation is enabled")
config_dict = {
"logging.default_log_level": get_variant(Str, "INFO"),
"server.insecure": get_variant(Str, "0")
}
# set all the values at once atomically
self._rhsm_config_proxy.SetAll(config_dict, "")
class TransferSubscriptionTokensTask(Task):
"""Transfer subscription tokens to the target system."""
RHSM_REPO_FILE_PATH = "/etc/yum.repos.d/redhat.repo"
RHSM_CONFIG_FILE_PATH = "/etc/rhsm/rhsm.conf"
RHSM_ENTITLEMENT_KEYS_PATH = "/etc/pki/entitlement"
RHSM_CONSUMER_KEY_PATH = "/etc/pki/consumer/key.pem"
RHSM_CONSUMER_CERT_PATH = "/etc/pki/consumer/cert.pem"
def __init__(self, sysroot, transfer_subscription_tokens):
"""Create a new task.
:param str sysroot: target system root path
:param bool transfer_subscription_tokens: if True attempt to transfer subscription
tokens to target system (we always transfer
system purpose data unconditionally)
"""
super().__init__()
self._sysroot = sysroot
self._transfer_subscription_tokens = transfer_subscription_tokens
@property
def name(self):
return "Transfer subscription tokens to target system"
def _copy_pem_files(self, input_folder, output_folder, not_empty=True):
"""Copy all pem files from input_folder to output_folder.
Files with the pem extension are generally encryption keys and certificates.
If output_folder does not exist, it & any parts of its path will
be created.
:param str input_folder: input folder for the pem files
:param str output_folder: output folder where to copy the pem files
:return: False if the input directory does not exists or is empty,
True after all pem files have be successfully copied
:rtype: bool
"""
# check the input folder exists
if not os.path.isdir(input_folder):
return False
# optionally check the input folder is not empty
if not_empty and not os.listdir(input_folder):
return False
# make sure the output folder exist
make_directories(output_folder)
# transfer all the pem files in the input folder
for pem_file_path in glob.glob(os.path.join(input_folder, "*.pem")):
shutil.copy(pem_file_path, output_folder)
# if we got this far the pem copy operation was a success
return True
def _copy_file(self, file_path, target_file_path):
if not os.path.isfile(file_path):
return False
# make sure the output folder exists
make_directories(os.path.dirname(target_file_path))
shutil.copy(file_path, target_file_path)
return True
def _transfer_file(self, target_path, target_name):
"""Transfer a file with nice logs and raise an exception if it does not exist."""
log.debug("subscription: transferring %s", target_name)
target_repo_file_path = join_paths(self._sysroot, target_path)
if not self._copy_file(target_path, target_repo_file_path):
msg = "{} ({}) is missing".format(target_name, self.RHSM_REPO_FILE_PATH)
raise SubscriptionTokenTransferError(msg)
def _transfer_system_purpose(self):
"""Transfer the system purpose file if present.
A couple notes:
- this might be needed even if the system has not been subscribed
during the installation and is therefore always attempted
- this means the syspurpose tool has been called in the installation
environment & we need to transfer the results to the target system
"""
if check_system_purpose_set(sysroot="/"):
log.debug("subscription: transferring syspurpose file")
target_syspurpose_file_path = self._sysroot + RHSM_SYSPURPOSE_FILE_PATH
self._copy_file(RHSM_SYSPURPOSE_FILE_PATH, target_syspurpose_file_path)
def _transfer_entitlement_keys(self):
"""Transfer the entitlement keys."""
log.debug("subscription: transferring entitlement keys")
target_entitlement_keys_path = self._sysroot + self.RHSM_ENTITLEMENT_KEYS_PATH
if not self._copy_pem_files(self.RHSM_ENTITLEMENT_KEYS_PATH, target_entitlement_keys_path):
msg = "RHSM entitlement keys (from {}) are missing.".format(
self.RHSM_ENTITLEMENT_KEYS_PATH)
raise SubscriptionTokenTransferError(msg)
def run(self):
"""Transfer the subscription tokens to the target system.
Otherwise the target system would have to be registered and subscribed again
due to missing subscription tokens.
"""
self._transfer_system_purpose()
# the other subscription tokens are only relevant if the system has been subscribed
if not self._transfer_subscription_tokens:
log.debug("subscription: transfer of subscription tokens not requested")
return
# transfer entitlement keys
self._transfer_entitlement_keys()
# transfer the consumer key
self._transfer_file(self.RHSM_CONSUMER_KEY_PATH, "RHSM consumer key")
# transfer the consumer cert
self._transfer_file(self.RHSM_CONSUMER_CERT_PATH, "RHSM consumer cert")
# transfer the redhat.repo file
self._transfer_file(self.RHSM_REPO_FILE_PATH, "RHSM repo file")
# transfer the RHSM config file
self._transfer_file(self.RHSM_CONFIG_FILE_PATH, "RHSM config file")