292 lines
8.3 KiB
Python
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
|