anaconda/anaconda-40.22.3.13/pyanaconda/modules/storage/devicetree/utils.py
2024-11-14 21:39:56 -08:00

292 lines
8.3 KiB
Python

#
# Copyright (C) 2014 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 time
import requests
from blivet import udev
from blivet.errors import StorageError
from blivet.formats import device_formats, get_format
from blivet.formats.fs import FS
from bytesize.bytesize import MiB, ROUND_UP
from pyanaconda.core import util
from pyanaconda.core.i18n import _
from pyanaconda.core.kernel import kernel_arguments
from pyanaconda.core.payload import parse_hdd_url
from pyanaconda.modules.common.constants.services import NETWORK
from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)
def is_supported_filesystem(fmt_type):
"""Is the filesystem supported?
Return True if the installer can create this filesystem.
Otherwise, return False.
:param fmt_type: a name of the formatting type
:return: True or False
"""
fmt = get_format(fmt_type)
return bool(
fmt.type
and fmt.supported
and fmt.formattable
and (isinstance(fmt, FS) or fmt.type in ["biosboot", "prepboot", "swap"])
and fmt.type not in ("ntfs", "tmpfs")
)
def get_supported_filesystems():
"""Get the supported filesystems.
Get a list of filesystems that can be created by the installer.
:return: a list of format types
"""
return list(filter(is_supported_filesystem, device_formats.keys()))
def download_escrow_certificate(url):
"""Download the escrow certificate.
:param url: an URL of the certificate
:return: a content of the certificate
"""
# Do we need a network connection?
if not url.startswith("/") and not url.startswith("file:"):
network_proxy = NETWORK.get_proxy()
if not network_proxy.Connected:
raise StorageError(
_("Escrow certificate {} requires the network.").format(url)
)
# Download the certificate.
log.info("Downloading an escrow certificate from: %s", url)
try:
request = util.requests_session().get(url, verify=True)
except requests.exceptions.SSLError as e:
raise StorageError(
_("SSL error while downloading the escrow certificate:\n\n{}").format(str(e))
) from e
except requests.exceptions.RequestException as e:
raise StorageError(
_("The following error was encountered while downloading "
"the escrow certificate:\n\n{}").format(str(e))
) from e
try:
certificate = request.content
finally:
request.close()
return certificate
def find_backing_device(devicetree, mount_point):
"""Find the backing device of the specified mount point.
:param devicetree: a device tree
:param mount_point: a mount point
:return: a device or None
"""
for line in open("/proc/mounts").readlines():
values = line.split()
if mount_point not in values:
continue
# Return the mounted device if available.
device_path = values[0]
device_name = device_path.split("/")[-1]
device = devicetree.get_device_by_name(device_name, hidden=True)
if device:
return device
# Or return the disk of the mounted device.
info = udev.get_device(device_node=device_path)
disk_name = udev.device_get_partition_disk(info) if info else ""
disk = devicetree.get_device_by_name(disk_name, hidden=True)
if disk:
return disk
return None
def find_stage2_device(devicetree):
"""Find the backing device of the stage2 image.
:param devicetree: a device tree
:return: a device or None
"""
url = kernel_arguments.get("stage2")
if url and url.startswith("hd:"):
device, _path = parse_hdd_url(url)
return devicetree.resolve_device(device)
return None
def get_required_device_size(required_space, format_class=None):
"""Get the required device size for the given space.
We need to provide information how big device is required to
have successful installation. The argument ``format_class``
should be filesystem format class for the **root** filesystem
this class carry information about metadata size.
:param required_space: the required space
:param format_class: the class of the filesystem format.
:returns: Size of the device with given filesystem format.
"""
if not format_class:
format_class = FS.biggest_overhead_FS()
device_size = format_class.get_required_size(required_space)
return device_size.round_to_nearest(MiB, ROUND_UP)
def find_optical_media(devicetree):
"""Find all devices with mountable optical media.
Search for devices identified as cdrom along with any other
device that has an iso9660 filesystem. This will catch USB
media created from ISO images.
:param devicetree: an instance of a device tree
:return: a list of devices
"""
devices = []
for device in devicetree.devices:
if device.type != "cdrom" and device.format.type != "iso9660":
continue
if not device.controllable:
continue
devicetree.handle_format(None, device)
if not hasattr(device.format, "mount"):
# no mountable media
continue
devices.append(device)
return devices
def find_mountable_partitions(devicetree):
"""Find all mountable partitions.
:param devicetree: an instance of a device tree
:return: a list of devices
"""
devices = []
for device in devicetree.devices:
if device.type != "partition":
continue
if not device.format.exists:
continue
if not device.format.mountable:
continue
devices.append(device)
return devices
def unlock_device(storage, device, passphrase):
"""Unlock a LUKS device.
:param storage: an instance of the storage
:param device: a device to unlock
:param passphrase: a passphrase to use
:return: True if success, otherwise False
"""
# Set the passphrase.
device.format.passphrase = passphrase
try:
# Unlock the device.
device.setup()
device.format.setup()
except StorageError as err:
log.error("Failed to unlock %s: %s", device.name, err)
# Teardown the device.
device.teardown(recursive=True)
# Forget the wrong passphrase.
device.format.passphrase = None
return False
else:
# Save the passphrase.
storage.save_passphrase(device)
# Set the passphrase also to the original format of the device.
device.original_format.passphrase = passphrase
# Wait for the device.
# Otherwise, we could get a message about no Linux partitions.
time.sleep(2)
# Update the device tree.
storage.devicetree.populate()
storage.devicetree.teardown_all()
return True
def find_unconfigured_luks(storage):
"""Find all unconfigured LUKS devices.
Returns a list of devices that require a passphrase
for their configuration.
:param storage: an instance of Blivet
:return: a list of devices
"""
devices = []
for device in storage.devices:
# Only LUKS devices.
if not device.format.type == "luks":
continue
# Skip existing formats.
if device.format.exists:
continue
# Skip formats with keys.
if device.format.has_key:
continue
devices.append(device)
return devices