# # The device tree scheduler # # Copyright (C) 2019 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. # from blivet import devicefactory from blivet.size import Size from pyanaconda.anaconda_loggers import get_module_logger from pyanaconda.modules.common.structures.validation import ValidationReport from pyanaconda.modules.storage.devicetree import DeviceTreeModule from pyanaconda.modules.storage.partitioning.interactive.add_device import AddDeviceTask from pyanaconda.modules.storage.partitioning.interactive.change_device import ChangeDeviceTask from pyanaconda.modules.storage.partitioning.interactive.interactive_partitioning import \ InteractiveAutoPartitioningTask from pyanaconda.modules.storage.partitioning.interactive.scheduler_interface import \ DeviceTreeSchedulerInterface from pyanaconda.modules.storage.partitioning.interactive import utils log = get_module_logger(__name__) __all__ = ["DeviceTreeSchedulerModule"] class DeviceTreeSchedulerModule(DeviceTreeModule): """The device tree scheduler.""" def for_publication(self): """Return a DBus representation.""" return DeviceTreeSchedulerInterface(self) def is_device(self, device_name): """Is the specified device in the device tree? It can recognize also hidden and incomplete devices. :param device_name: a name of the device :return: True or False """ device = self.storage.devicetree.get_device_by_name( device_name, hidden=True, incomplete=True ) return device is not None def is_device_locked(self, device_name): """Is the specified device locked? :param device_name: a name of the device :return: True or False """ device = self._get_device(device_name) return device.format.type == "luks" and device.format.exists def is_device_editable(self, device_name): """Is the specified device editable? :param device_name: a name of the device :return: True or False """ device = self._get_device(device_name) return devicefactory.get_device_type(device) is not None def check_completeness(self, device_name): """Check that the specified device is complete. :param device_name: a name of the device :return: a validation report """ report = ValidationReport() device = self._get_device(device_name) message = utils.check_device_completeness(device) if message: report.error_messages.append(message) return report def get_default_file_system(self): """Get the default type of a filesystem. :return: a filesystem name """ return self.storage.default_fstype def get_default_luks_version(self): """Get the default version of LUKS. :return: a version of LUKS """ return self.storage.default_luks_version def get_container_free_space(self, container_name): """Get total free space in the specified container. :param container_name: a name of the container :return: a size in bytes """ container = self._get_device(container_name) return Size(getattr(container, "free_space", 0)).get_bytes() def generate_system_name(self): """Generate a name of the new installation. :return: a translated string """ return utils.get_new_root_name() def generate_system_data(self, boot_drive): """Generate the new installation data. :param boot_drive: a name of the boot drive :return: an instance of OSData """ root = utils.create_new_root(self.storage, boot_drive) return self._get_os_data(root) def generate_device_name(self, mount_point, format_type): """Get a suggestion for a device name. :param mount_point: a mount point :param format_type: a format type :return: a generated device name """ return self.storage.suggest_device_name( mountpoint=mount_point, swap=bool(format_type == "swap") ) def generate_container_name(self): """Get a suggestion for a container name. :return: a generated container name """ return self._storage.suggest_container_name() def generate_device_factory_request(self, device_name): """Generate a device factory request for the given device. The request will reflect the current state of the device. It can be modified and used to change the device. :param device_name: a device name :return: a device factory request """ device = self._get_device(device_name) return utils.generate_device_factory_request(self.storage, device) def generate_device_factory_permissions(self, request): """Generate device factory permissions for the given request. The permissions will reflect which device attributes we are allowed to change in the requested device. :param request: a device factory request :return: device factory permissions """ return utils.generate_device_factory_permissions(self.storage, request) def generate_container_data(self, request): """Generate the container data for the device factory request. :param request: a device factory request """ utils.generate_container_data(self.storage, request) def update_container_data(self, request, container_name): """Update the container data in the device factory request. :param request: a device factory request :param container_name: a container name """ utils.update_container_data(self.storage, request, container_name) def collect_new_devices(self, boot_drive): """Get all new devices in the device tree. FIXME: Remove the boot drive option. :param boot_drive: a name of the boot drive :return: a list of device names """ return [d.name for d in utils.collect_new_devices(self.storage, boot_drive)] def collect_unused_devices(self): """Collect all devices that are not used in existing or new installations. :return: a list of device names """ return [d.name for d in utils.collect_unused_devices(self.storage)] def collect_unused_mount_points(self): """Collect mount points that can be assigned to a device. :return: a list of mount points """ return [m for m in utils.collect_mount_points() if m not in self.storage.mountpoints] def collect_containers(self, device_type): """Collect containers of the given type. :param device_type: a device type :return: a list of container names """ return [c.name for c in utils.collect_containers(self.storage, device_type)] def collect_supported_systems(self): """Collect supported existing or new installations. :return: a list of data about found installations """ return list(map(self._get_os_data, utils.collect_roots(self.storage))) def get_device_types_for_device(self, device_name): """Collect supported device types for the given device. :param device_name: a device name :return: a list of device types """ device = self._get_device(device_name) return utils.collect_device_types(device) def get_file_systems_for_device(self, device_name): """Get supported file system types for the given device. :param device_name: a device name :return: a list of file system names """ device = self._get_device(device_name) return utils.collect_file_system_types(device) def get_supported_raid_levels(self, device_type): """Get RAID levels for the specified device type. :param device_type: a type of the device :return: a list of RAID level names """ return sorted([level.name for level in utils.get_supported_raid_levels(device_type)]) def validate_mount_point(self, mount_point): """Validate the given mount point. :param mount_point: a path to a mount point :return: a validation report """ report = ValidationReport() mount_points = self.storage.mountpoints.keys() error = utils.validate_mount_point(mount_point, mount_points) if error: report.error_messages.append(error) return report def validate_raid_level(self, raid_level, num_members): """Validate the given RAID level. :param raid_level: a RAID level name :param num_members: a number of members :return: a validation report """ report = ValidationReport() raid_level = utils.get_raid_level_by_name(raid_level) error = utils.validate_raid_level(raid_level, num_members) if error: report.error_messages.append(error) return report def validate_container_name(self, name): """Validate the given container name. :param name: a container name :return: a validation report """ report = ValidationReport() error = utils.validate_container_name(self.storage, name) if error: report.error_messages.append(error) return report def validate_device_factory_request(self, request): """Validate the given device factory request. :param request: a device factory request :return: a validation report """ report = ValidationReport() error = utils.validate_device_factory_request(self.storage, request) if error: report.error_messages.append(error) return report def add_device(self, request): """Add a new device to the storage model. :param request: a device factory request :raise: StorageConfigurationError if the device cannot be created """ task = AddDeviceTask(self.storage, request) task.run() def change_device(self, request, original_request): """Change a device in the storage model. FIXME: Remove the original request from the arguments. :param request: a device factory request :param original_request: an original device factory request :raise: StorageConfigurationError if the device cannot be changed """ device = self._get_device(request.device_spec) task = ChangeDeviceTask(self.storage, device, request, original_request) task.run() def reset_device(self, device_name): """Reset the specified device in the storage model. FIXME: Merge with destroy_device. :param device_name: a name of the device :raise: StorageConfigurationError in case of failure """ device = self._get_device(device_name) utils.reset_device(self.storage, device) def destroy_device(self, device_name): """Destroy the specified device in the storage model. :param device_name: a name of the device :raise: StorageConfigurationError in case of failure """ device = self._get_device(device_name) utils.destroy_device(self.storage, device) def schedule_partitions_with_task(self, request): """Schedule the partitioning actions. Generate the automatic partitioning configuration using the given request. :param: a partitioning request :return: a task """ return InteractiveAutoPartitioningTask(self.storage, request)