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 get_snapshot_device(request, devicetree): """Get the ThinLV snapshot device. :param request: a snapshot request :param devicetree: a device tree to look up devices :return: a model of the ThinLV snapshot """ snap_name = request.name.replace('-', '--') origin = request.origin.replace('-', '--').replace('/', '-') origin_dev = devicetree.get_device_by_name(origin) log.debug("Snapshot: name %s has origin %s", request.name, origin_dev) if origin_dev is None: raise StorageError( _("Snapshot: origin \"{}\" doesn't exist!").format(request.origin)) if not origin_dev.is_thin_lv: raise StorageError( _("Snapshot: origin \"{}\" of snapshot \"{}\" is not a valid " "thin LV device.").format(request.origin, request.name)) if devicetree.get_device_by_name("%s-%s" % (origin_dev.vg.name, snap_name)): raise StorageError( _("Snapshot {} already exists.").format(request.name)) try: return LVMLogicalVolumeDevice(name=request.name, parents=[origin_dev.pool], seg_type="thin", origin=origin_dev) except ValueError as e: raise StorageError(str(e)) from e
def _add_device(self, storage, request: DeviceFactoryRequest, use_existing_container=False): """Add a device to the storage model. :param storage: an instance of Blivet :param request: a device factory request :param use_existing_container: should we use an existing container? :raise: StorageError if the device cannot be created """ # Create the device factory. factory = devicefactory.get_device_factory( storage, device_type=request.device_type, size=Size(request.device_size) if request.device_size else None) # Find a container. container = factory.get_container( allow_existing=use_existing_container) if use_existing_container and not container: raise StorageError("No existing container found.") # Update the device info. if container: # Don't override user-initiated changes to a defined container. request.disks = [d.name for d in container.disks] request.container_encrypted = container.encrypted request.container_raid_level = get_device_raid_level_name( container) request.container_size_policy = get_container_size_policy( container) # The existing container has a name. if use_existing_container: request.container_name = container.name # The container is already encrypted if container.encrypted: request.device_encrypted = False # Create the device. dev_info = get_device_factory_arguments(storage, request) try: storage.factory_device(**dev_info) except StorageError as e: log.error("The device creation has failed: %s", e) raise except OverflowError as e: log.error("Invalid partition size set: %s", str(e)) raise StorageError( "Invalid partition size set. Use a valid integer.") from None
def _add_device(storage, dev_info, use_existing_container=False): """Add a device to the storage model. :param storage: an instance of Blivet :param dev_info: a device info :param use_existing_container: should we use an existing container? :raise: StorageError if the device cannot be created """ # Create the device factory. factory = devicefactory.get_device_factory( storage, device_type=dev_info["device_type"], size=dev_info["size"], ) # Find a container. container = factory.get_container(allow_existing=use_existing_container) if use_existing_container and not container: raise StorageError("No existing container found.") # Update the device info. if container: # Don't override user-initiated changes to a defined container. dev_info["disks"] = container.disks dev_info.update({ "container_encrypted": container.encrypted, "container_raid_level": get_device_raid_level(container), "container_size": getattr(container, "size_policy", container.size) }) # The existing container has a name. if use_existing_container: dev_info["container_name"] = container.name # The container is already encrypted if container.encrypted: dev_info["encrypted"] = False # Create the device. try: storage.factory_device(**dev_info) except StorageError as e: log.error("The device creation has failed: %s", e) raise except OverflowError as e: log.error("Invalid partition size set: %s", str(e)) raise StorageError( "Invalid partition size set. Use a valid integer.") from None
def reformat_device(storage, device, fstype, mountpoint, label): """Reformat the given device. :param storage: an instance of Blivet :param device: a device to reformat :param fstype: a file system type :param mountpoint: a mount point :param label: a label :raise: StorageError if we fail to format the device """ log.info("scheduling reformat of %s as %s", device.name, fstype) old_format = device.format new_format = get_format( fstype, mountpoint=mountpoint, label=label, device=device.path ) try: storage.format_device(device, new_format) except (StorageError, ValueError) as e: log.error("failed to register device format action: %s", e) device.format = old_format raise StorageError(str(e)) from None
def test_unlock_device(self, device_setup, device_teardown, format_setup): """Test UnlockDevice.""" self.storage.devicetree.populate = Mock() self.storage.devicetree.teardown_all = Mock() dev1 = StorageDevice("dev1", fmt=get_format("ext4"), size=Size("10 GiB")) self._add_device(dev1) dev2 = LUKSDevice("dev2", parents=[dev1], fmt=get_format("luks"), size=Size("10 GiB")) self._add_device(dev2) self.assertEqual(self.interface.UnlockDevice("dev2", "passphrase"), True) device_setup.assert_called_once() format_setup.assert_called_once() device_teardown.assert_not_called() self.storage.devicetree.populate.assert_called_once() self.storage.devicetree.teardown_all.assert_called_once() self.assertTrue(dev2.format.has_key) device_setup.side_effect = StorageError("Fake error") self.assertEqual(self.interface.UnlockDevice("dev2", "passphrase"), False) device_teardown.assert_called_once() self.assertFalse(dev2.format.has_key)
def update_container_data(storage, request: DeviceFactoryRequest, container_name): """Update the container data in the device factory request. :param storage: an instance of Blivet :param request: a device factory request :param container_name: a container name to apply """ # Reset all container data. request.reset_container_data() # Check the device type. if request.device_type not in CONTAINER_DEVICE_TYPES: raise StorageError("Invalid device type.") # Find the container in the device tree if any. container = storage.devicetree.get_device_by_name(container_name) if container: # Set the request from the found container. request.container_spec = container.name request.container_name = container.name request.container_encrypted = container.encrypted request.container_raid_level = get_device_raid_level_name(container) request.container_size_policy = get_container_size_policy(container) # Use the container's disks. request.disks = [d.name for d in container.disks] else: # Set the request from the new container. request.container_name = container_name request.container_raid_level = get_default_container_raid_level_name( request.device_type)
def rename_container(storage, container, name): """Rename the given container. :param storage: an instance of Blivet :param container: an instance of a container :param name: a new name of the container """ # Remove the names of the container and its child # devices from the list of already-used names. for device in [container] + container.children: if device.name in storage.devicetree.names: storage.devicetree.names.remove(device.name) luks_name = "luks-%s" % device.name if luks_name in storage.devicetree.names: storage.devicetree.names.remove(luks_name) # Set the name of the container. try: container.name = name except ValueError as e: raise StorageError(str(e)) from None # Fix the btrfs label. if container.format.type == "btrfs": container.format.label = name # Add the new names to the list of the already-used # names and prevent potential issues with making the # devices encrypted later for device in [container] + container.children: storage.devicetree.names.append(device.name) luks_name = "luks-%s" % device.name storage.devicetree.names.append(luks_name)
def resize_device(storage, device, new_size, old_size): """Resize the given device. :param storage: an instance of Blivet :param device: a device to resize :param new_size: a new size :param old_size: an old size :return: True if the device changed its size, otherwise False :raise: StorageError if we fail to schedule the device resize """ # If a LUKS device is being displayed, adjust the size # to the appropriate size for the raw device. use_size = new_size use_old_size = old_size if device.raw_device is not device: use_size = new_size + crypto.LUKS_METADATA_SIZE use_old_size = device.raw_device.size # Bound size to boundaries given by the device. use_size = device.raw_device.align_target_size(use_size) use_size = bound_size(use_size, device.raw_device, use_old_size) use_size = device.raw_device.align_target_size(use_size) # And then we need to re-check that the max size is actually # different from the current size. if use_size == device.size or use_size == device.raw_device.size: # The size hasn't changed. log.debug("canceled resize of device %s to %s", device.raw_device.name, use_size) return False if new_size == device.current_size or use_size == device.current_size: # The size has been set back to its original value. log.debug("removing resize of device %s", device.raw_device.name) actions = storage.devicetree.actions.find(action_type="resize", devid=device.raw_device.id) for action in reversed(actions): storage.devicetree.actions.remove(action) return bool(actions) else: # the size has changed log.debug("scheduling resize of device %s to %s", device.raw_device.name, use_size) try: storage.resize_device(device.raw_device, use_size) except (StorageError, ValueError) as e: log.error("failed to schedule device resize: %s", e) device.raw_device.size = use_old_size raise StorageError(str(e)) from None log.debug("new size: %s", device.raw_device.size) log.debug("target size: %s", device.raw_device.target_size) return True
def _setup_mount_point(self, storage, mount_data): """Set up a mount point. :param storage: an instance of the Blivet's storage object :param mount_data: an instance of MountPointRequest """ device_spec = mount_data.device_spec reformat = mount_data.reformat format_type = mount_data.format_type device = storage.devicetree.resolve_device(device_spec) if device is None: raise StorageError( _("Unknown or invalid device '{}' specified").format( device_spec)) if reformat: if format_type: fmt = get_format(format_type) if not fmt: raise StorageError( _("Unknown or invalid format '{}' specified for " "device '{}'").format(format_type, device_spec)) else: old_fmt = device.format if not old_fmt or old_fmt.type is None: raise StorageError( _("No format on device '{}'").format(device_spec)) fmt = get_format(old_fmt.type) storage.format_device(device, fmt) # make sure swaps end up in /etc/fstab if fmt.type == "swap": storage.add_fstab_swap(device) # only set mount points for mountable formats mount_point = mount_data.mount_point if device.format.mountable and mount_point and mount_point != "none": device.format.mountpoint = mount_point device.format.create_options = mount_data.format_options device.format.options = mount_data.mount_options
def _verify_partitioning(self, storage): """Verify the created partitioning.""" report = storage_checker.check(storage, skip=(verify_luks_devices_have_key, )) report.log(log) if not report.errors: return raise StorageError(" ".join(report.errors))
def _change_device_name(self): """Change the device name.""" name = self._request.device_name original_name = self._original_request.device_name if name == original_name: return log.debug("Changing device name: %s", name) try: self._device.raw_device.name = name except ValueError as e: log.error("Invalid device name: %s", e) raise StorageError(str(e)) from e
def test_verify_requests(self, device_getter): """Test the verify_requests method.""" report_error = Mock() report_warning = Mock() self.module._requests = [Mock(when=SNAPSHOT_WHEN_POST_INSTALL)] # Test passing check. self.module.verify_requests(Mock(), Mock(), report_error, report_warning) report_error.assert_not_called() report_warning.assert_not_called() # Test failing check. device_getter.side_effect = StorageError("Fake error") self.module.verify_requests(Mock(), Mock(), report_error, report_warning) report_error.assert_called_once_with("Fake error") report_warning.assert_not_called()
def rename_container(storage, container, name): """Rename the given container. :param storage: an instance of Blivet :param container: an instance of a container :param name: a new name of the container """ log.debug("Rename container %s to %s.", container.name, name) try: container.name = name except ValueError as e: raise StorageError(str(e)) from None # Fix the btrfs label. if container.format.type == "btrfs": container.format.label = name
def get_container(storage, device_type, device=None): """Get a container of the given type. :param storage: an instance of Blivet :param device_type: a device type :param device: a defined factory device or None :return: a container device """ if device_type not in CONTAINER_DEVICE_TYPES: raise StorageError("Invalid device type {}".format(device_type)) if device and devicefactory.get_device_type(device) != device_type: device = None factory = devicefactory.get_device_factory( storage, device_type=device_type, size=Size(0), ) return factory.get_container(device=device)
def _rename_container(self): """Rename the existing container.""" container_spec = self._request.container_spec container_name = self._request.container_name # Nothing to do. if not container_spec or container_spec == container_name: return container = self._storage.devicetree.resolve_device(container_spec) # Container doesn't exist. if not container: return log.debug("Changing container name: %s", container_name) try: rename_container(self._storage, container, container_name) except StorageError as e: log.error("Invalid container name: %s", e) raise StorageError(str(e)) from e
def _execute_raid_data(self, storage, data, raid_data): """Execute the raid data. :param storage: an instance of the Blivet's storage object :param data: an instance of kickstart data :param raid_data: an instance of RaidData """ raidmems = [] devicetree = storage.devicetree devicename = raid_data.device if raid_data.preexist: device = devicetree.resolve_device(devicename) if device: devicename = device.name kwargs = {} if raid_data.mountpoint == "swap": ty = "swap" raid_data.mountpoint = "" elif raid_data.mountpoint.startswith("pv."): ty = "lvmpv" kwargs["name"] = raid_data.mountpoint data.onPart[kwargs["name"]] = devicename if devicetree.get_device_by_name(kwargs["name"]): raise StorageError( _("PV partition \"{}\" is defined multiple " "times.").format(kwargs["name"]) ) raid_data.mountpoint = "" elif raid_data.mountpoint.startswith("btrfs."): ty = "btrfs" kwargs["name"] = raid_data.mountpoint data.onPart[kwargs["name"]] = devicename if devicetree.get_device_by_name(kwargs["name"]): raise StorageError( _("Btrfs partition \"{}\" is defined multiple " "times.").format(kwargs["name"]) ) raid_data.mountpoint = "" else: if raid_data.fstype != "": ty = raid_data.fstype elif (raid_data.mountpoint == "/boot" and "mdarray" in storage.bootloader.stage2_device_types): ty = storage.default_boot_fstype else: ty = storage.default_fstype # Sanity check mountpoint self._check_mount_point(raid_data.mountpoint) # If this specifies an existing request that we should not format, # quit here after setting up enough information to mount it later. if not raid_data.format: if not devicename: raise StorageError( _("raid --noformat must also use the --device option.") ) dev = devicetree.get_device_by_name(devicename) if not dev: raise StorageError( _("RAID device \"{}\" given in raid command does " "not exist.").format(devicename) ) dev.format.mountpoint = raid_data.mountpoint dev.format.mountopts = raid_data.fsopts if ty == "swap": storage.add_fstab_swap(dev) return # Get a list of all the RAID members. for member in raid_data.members: dev = devicetree.resolve_device(member) if not dev: # if member is using --onpart, use original device mem = data.onPart.get(member, member) dev = devicetree.resolve_device(mem) or lookup_alias(devicetree, member) if dev and dev.format.type == "luks": try: dev = dev.children[0] except IndexError: dev = None if dev and dev.format.type != "mdmember": raise StorageError( _("RAID device \"{}\" has a format of \"{}\", but should have " "a format of \"mdmember\".").format(member, dev.format.type) ) if not dev: raise StorageError( _("Tried to use undefined partition \"{}\" in RAID " "specification.").format(member) ) raidmems.append(dev) # Now get a format to hold a lot of these extra values. kwargs["fmt"] = get_format( ty, label=raid_data.label, fsprofile=raid_data.fsprofile, mountpoint=raid_data.mountpoint, mountopts=raid_data.fsopts, create_options=raid_data.mkfsopts ) if not kwargs["fmt"].type: raise StorageError( _("The \"{}\" file system type is not supported.").format(ty) ) kwargs["name"] = devicename kwargs["level"] = raid_data.level kwargs["parents"] = raidmems kwargs["member_devices"] = len(raidmems) - raid_data.spares kwargs["total_devices"] = len(raidmems) if raid_data.chunk_size: kwargs["chunk_size"] = Size("%d KiB" % raid_data.chunk_size) add_fstab_swap = None # If we were given a pre-existing RAID to create a filesystem on, # we need to verify it exists and then schedule a new format action # to take place there. Also, we only support a subset of all the # options on pre-existing RAIDs. if raid_data.preexist: device = devicetree.get_device_by_name(devicename) if not device: raise StorageError( _("RAID volume \"{}\" specified with --useexisting does " "not exist.").format(devicename) ) storage.devicetree.recursive_remove(device, remove_device=False) devicetree.actions.add(ActionCreateFormat(device, kwargs["fmt"])) if ty == "swap": add_fstab_swap = device else: if devicename and devicename in (a.name for a in storage.mdarrays): raise StorageError( _("The RAID volume name \"{}\" is already in use.").format(devicename) ) # If a previous device has claimed this mount point, delete the # old one. try: if raid_data.mountpoint: device = storage.mountpoints[raid_data.mountpoint] storage.destroy_device(device) except KeyError: pass request = storage.new_mdarray(**kwargs) storage.create_device(request) if ty == "swap": add_fstab_swap = request if raid_data.encrypted: passphrase = self._get_passphrase(raid_data) cert = storage.get_escrow_certificate(raid_data.escrowcert) # Get the version of LUKS and PBKDF arguments. raid_data.luks_version = raid_data.luks_version or storage.default_luks_version pbkdf_args = get_pbkdf_args( luks_version=raid_data.luks_version, pbkdf_type=raid_data.pbkdf, max_memory_kb=raid_data.pbkdf_memory, iterations=raid_data.pbkdf_iterations, time_ms=raid_data.pbkdf_time ) if pbkdf_args and not luks_data.pbkdf_args: luks_data.pbkdf_args = pbkdf_args if raid_data.preexist: luksformat = kwargs["fmt"] device.format = get_format( "luks", passphrase=passphrase, device=device.path, cipher=raid_data.cipher, escrow_cert=cert, add_backup_passphrase=raid_data.backuppassphrase, luks_version=raid_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice( "luks%d" % storage.next_id, fmt=luksformat, parents=device ) else: luksformat = request.format request.format = get_format( "luks", passphrase=passphrase, cipher=raid_data.cipher, escrow_cert=cert, add_backup_passphrase=raid_data.backuppassphrase, luks_version=raid_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice( "luks%d" % storage.next_id, fmt=luksformat, parents=request ) if ty == "swap": # swap is on the LUKS device instead of the parent device, # override the device here add_fstab_swap = luksdev storage.create_device(luksdev) if add_fstab_swap: storage.add_fstab_swap(add_fstab_swap)
def _get_size(self, number, unit): """Get a size from the given number and unit.""" try: return Size("{} {}".format(number, unit)) except ValueError as e: raise StorageError(_("The size \"{}\" is invalid.").format(number)) from e
def _execute_volgroup_data(self, storage, data, volgroup_data): """Execute the volgroup data. :param storage: an instance of the Blivet's storage object :param data: an instance of kickstart data :param volgroup_data: an instance of VolGroupData """ pvs = [] devicetree = storage.devicetree # Get a list of all the physical volume devices that make up this VG. for pv in volgroup_data.physvols: dev = devicetree.resolve_device(pv) if not dev: # if pv is using --onpart, use original device pv_name = data.onPart.get(pv, pv) dev = devicetree.resolve_device(pv_name) or lookup_alias(devicetree, pv) if dev and dev.format.type == "luks": try: dev = dev.children[0] except IndexError: dev = None if dev and dev.format.type != "lvmpv": raise StorageError( _("Physical volume \"{}\" has a format of \"{}\", but should " "have a format of \"lvmpv\".").format(pv, dev.format.type) ) if not dev: raise StorageError( _("Tried to use undefined partition \"{}\" in Volume Group " "specification").format(pv) ) pvs.append(dev) if len(pvs) == 0 and not volgroup_data.preexist: raise StorageError( _("Volume group \"{}\" defined without any physical volumes. Either specify " "physical volumes or use --useexisting.").format(volgroup_data.vgname) ) if volgroup_data.pesize == 0: # default PE size requested -- we use blivet's default in KiB volgroup_data.pesize = LVM_PE_SIZE.convert_to(KiB) pesize = Size("%d KiB" % volgroup_data.pesize) possible_extents = LVMVolumeGroupDevice.get_supported_pe_sizes() if pesize not in possible_extents: raise StorageError( _("Volume group given physical extent size of \"{}\", but must be one " "of:\n{}.").format(pesize, ", ".join(str(e) for e in possible_extents)) ) # If --noformat or --useexisting was given, there's really nothing to do. if not volgroup_data.format or volgroup_data.preexist: if not volgroup_data.vgname: raise StorageError( _("volgroup --noformat and volgroup --useexisting must " "also use the --name= option.") ) dev = devicetree.get_device_by_name(volgroup_data.vgname) if not dev: raise StorageError( _("Volume group \"{}\" given in volgroup command does " "not exist.").format(volgroup_data.vgname) ) elif volgroup_data.vgname in (vg.name for vg in storage.vgs): raise StorageError( _("The volume group name \"{}\" is already " "in use.").format(volgroup_data.vgname) ) else: request = storage.new_vg( parents=pvs, name=volgroup_data.vgname, pe_size=pesize ) storage.create_device(request) if volgroup_data.reserved_space: request.reserved_space = Size("{:d} MiB".format(volgroup_data.reserved_space)) elif volgroup_data.reserved_percent: request.reserved_percent = volgroup_data.reserved_percent # in case we had to truncate or otherwise adjust the specified name data.onPart[volgroup_data.vgname] = request.name
def _execute_logvol_data(self, storage, data, logvol_data): """Execute the logvol data. :param storage: an instance of the Blivet's storage object :param data: an instance of kickstart data :param logvol_data: an instance of LogVolData """ devicetree = storage.devicetree # FIXME: we should be running sanityCheck on partitioning that is not ks # autopart, but that's likely too invasive for #873135 at this moment if logvol_data.mountpoint == "/boot" and blivet.arch.is_s390(): raise StorageError( _("/boot cannot be of type \"lvmlv\" on s390x") ) # we might have truncated or otherwise changed the specified vg name vgname = data.onPart.get(logvol_data.vgname, logvol_data.vgname) size = None if logvol_data.percent: size = Size(0) if logvol_data.mountpoint == "swap": ty = "swap" logvol_data.mountpoint = "" if logvol_data.recommended or logvol_data.hibernation: disk_space = self._disk_free_space size = suggest_swap_size( hibernation=logvol_data.hibernation, disk_space=disk_space ) logvol_data.grow = False else: if logvol_data.fstype != "": ty = logvol_data.fstype else: ty = storage.default_fstype if size is None and not logvol_data.preexist: if not logvol_data.size: raise StorageError( _("Size cannot be decided on from kickstart nor obtained from device.") ) size = self._get_size(logvol_data.size, "MiB") if logvol_data.thin_pool: logvol_data.mountpoint = "" ty = None # Sanity check mountpoint self._check_mount_point(logvol_data.mountpoint) # Check that the VG this LV is a member of has already been specified. vg = devicetree.get_device_by_name(vgname) if not vg: raise StorageError( _("No volume group exists with the name \"{}\". Specify volume " "groups before logical volumes.").format(logvol_data.vgname) ) # If cache PVs specified, check that they belong to the same VG this LV is a member of if logvol_data.cache_pvs: pv_devices = self._get_cache_pv_devices(devicetree, logvol_data) if not all(pv in vg.pvs for pv in pv_devices): raise StorageError( _("Cache PVs must belong to the same VG as the cached LV") ) pool = None if logvol_data.thin_volume: pool = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.pool_name)) if not pool: raise StorageError( _("No thin pool exists with the name \"{}\". Specify thin pools " "before thin volumes.").format(logvol_data.pool_name) ) # If this specifies an existing request that we should not format, # quit here after setting up enough information to mount it later. if not logvol_data.format: if not logvol_data.name: raise StorageError( _("logvol --noformat must also use the --name= option.") ) dev = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name)) if not dev: raise StorageError( _("Logical volume \"{}\" given in logvol command does " "not exist.").format(logvol_data.name) ) if logvol_data.resize: size = dev.raw_device.align_target_size(size) if size < dev.currentSize: # shrink try: devicetree.actions.add(ActionResizeFormat(dev, size)) devicetree.actions.add(ActionResizeDevice(dev, size)) except ValueError as e: self._handle_invalid_target_size(e, logvol_data.size, dev.name) else: # grow try: devicetree.actions.add(ActionResizeDevice(dev, size)) devicetree.actions.add(ActionResizeFormat(dev, size)) except ValueError as e: self._handle_invalid_target_size(e, logvol_data.size, dev.name) dev.format.mountpoint = logvol_data.mountpoint dev.format.mountopts = logvol_data.fsopts if ty == "swap": storage.add_fstab_swap(dev) return # Make sure this LV name is not already used in the requested VG. if not logvol_data.preexist: tmp = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name)) if tmp: raise StorageError( _("Logical volume name \"{}\" is already in use in volume group " "\"{}\".").format(logvol_data.name, vg.name) ) if not logvol_data.percent and size and not logvol_data.grow and size < vg.pe_size: raise StorageError( _("Logical volume size \"{}\" must be larger than the volume " "group extent size of \"{}\".").format(size, vg.pe_size) ) # Now get a format to hold a lot of these extra values. fmt = get_format( ty, mountpoint=logvol_data.mountpoint, label=logvol_data.label, fsprofile=logvol_data.fsprofile, create_options=logvol_data.mkfsopts, mountopts=logvol_data.fsopts ) if not fmt.type and not logvol_data.thin_pool: raise StorageError( _("The \"{}\" file system type is not supported.").format(ty) ) add_fstab_swap = None # If we were given a pre-existing LV to create a filesystem on, we need # to verify it and its VG exists and then schedule a new format action # to take place there. Also, we only support a subset of all the # options on pre-existing LVs. if logvol_data.preexist: device = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name)) if not device: raise StorageError( _("Logical volume \"{}\" given in logvol command does " "not exist.").format(logvol_data.name) ) storage.devicetree.recursive_remove(device, remove_device=False) if logvol_data.resize: size = device.raw_device.align_target_size(size) try: devicetree.actions.add(ActionResizeDevice(device, size)) except ValueError as e: self._handle_invalid_target_size(e, logvol_data.size, device.name) devicetree.actions.add(ActionCreateFormat(device, fmt)) if ty == "swap": add_fstab_swap = device else: # If a previous device has claimed this mount point, delete the # old one. try: if logvol_data.mountpoint: device = storage.mountpoints[logvol_data.mountpoint] storage.destroy_device(device) except KeyError: pass if logvol_data.thin_volume: parents = [pool] else: parents = [vg] pool_args = {} if logvol_data.thin_pool: if logvol_data.profile: matching = (p for p in KNOWN_THPOOL_PROFILES if p.name == logvol_data.profile) profile = next(matching, None) if profile: pool_args["profile"] = profile else: log.warning( "No matching profile for %s found in LVM configuration", logvol_data.profile ) if logvol_data.metadata_size: pool_args["metadata_size"] = Size("%d MiB" % logvol_data.metadata_size) if logvol_data.chunk_size: pool_args["chunk_size"] = Size("%d KiB" % logvol_data.chunk_size) if logvol_data.maxSizeMB: maxsize = self._get_size(logvol_data.maxSizeMB, "MiB") else: maxsize = None if logvol_data.cache_size and logvol_data.cache_pvs: pv_devices = self._get_cache_pv_devices(devicetree, logvol_data) cache_size = Size("%d MiB" % logvol_data.cache_size) cache_mode = logvol_data.cache_mode or None cache_request = LVMCacheRequest(cache_size, pv_devices, cache_mode) else: cache_request = None request = storage.new_lv( fmt=fmt, name=logvol_data.name, parents=parents, size=size, thin_pool=logvol_data.thin_pool, thin_volume=logvol_data.thin_volume, grow=logvol_data.grow, maxsize=maxsize, percent=logvol_data.percent, cache_request=cache_request, **pool_args ) storage.create_device(request) if ty == "swap": add_fstab_swap = request if logvol_data.encrypted: passphrase = self._get_passphrase(logvol_data) cert = storage.get_escrow_certificate(logvol_data.escrowcert) # Get the version of LUKS and PBKDF arguments. logvol_data.luks_version = logvol_data.luks_version or storage.default_luks_version pbkdf_args = get_pbkdf_args( luks_version=logvol_data.luks_version, pbkdf_type=logvol_data.pbkdf, max_memory_kb=logvol_data.pbkdf_memory, iterations=logvol_data.pbkdf_iterations, time_ms=logvol_data.pbkdf_time ) if pbkdf_args and not luks_data.pbkdf_args: luks_data.pbkdf_args = pbkdf_args if logvol_data.preexist: luksformat = fmt device.format = get_format( "luks", passphrase=passphrase, device=device.path, cipher=logvol_data.cipher, escrow_cert=cert, add_backup_passphrase=logvol_data.backuppassphrase, luks_version=logvol_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice( "luks%d" % storage.next_id, fmt=luksformat, parents=device ) else: luksformat = request.format request.format = get_format( "luks", passphrase=passphrase, cipher=logvol_data.cipher, escrow_cert=cert, add_backup_passphrase=logvol_data.backuppassphrase, luks_version=logvol_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice( "luks%d" % storage.next_id, fmt=luksformat, parents=request ) if ty == "swap": # swap is on the LUKS device not on the LUKS' parent device, # override the info here add_fstab_swap = luksdev storage.create_device(luksdev) if add_fstab_swap: storage.add_fstab_swap(add_fstab_swap)
def _execute_btrfs_data(self, storage, data, btrfs_data): """Execute the btrfs command. :param storage: an instance of the Blivet's storage object :param data: an instance of kickstart data :param btrfs_data: an instance of BTRFSData """ devicetree = storage.devicetree members = [] # Get a list of all the devices that make up this volume. for member in btrfs_data.devices: dev = devicetree.resolve_device(member) if not dev: # if using --onpart, use original device member_name = data.onPart.get(member, member) dev = devicetree.resolve_device(member_name) or lookup_alias(devicetree, member) if dev and dev.format.type == "luks": try: dev = dev.children[0] except IndexError: dev = None if dev and dev.format.type != "btrfs": raise StorageError( _("Btrfs partition \"{}\" has a format of \"{}\", but should " "have a format of \"btrfs\".").format(member, dev.format.type) ) if not dev: raise StorageError( _("Tried to use undefined partition \"{}\" in Btrfs volume " "specification.").format(member) ) members.append(dev) if btrfs_data.subvol: name = btrfs_data.name elif btrfs_data.label: name = btrfs_data.label else: name = None if len(members) == 0 and not btrfs_data.preexist: raise StorageError( _("Btrfs volume defined without any member devices. " "Either specify member devices or use --useexisting.") ) # allow creating btrfs vols/subvols without specifying mountpoint if btrfs_data.mountpoint in ("none", "None"): btrfs_data.mountpoint = "" # Sanity check mountpoint self._check_mount_point(btrfs_data.mountpoint) # If a previous device has claimed this mount point, delete the # old one. try: if btrfs_data.mountpoint: device = storage.mountpoints[btrfs_data.mountpoint] storage.destroy_device(device) except KeyError: pass if btrfs_data.preexist: device = devicetree.resolve_device(btrfs_data.name) if not device: raise StorageError( _("Btrfs volume \"{}\" specified with --useexisting " "does not exist.").format(btrfs_data.name) ) device.format.mountpoint = btrfs_data.mountpoint else: request = storage.new_btrfs( name=name, subvol=btrfs_data.subvol, mountpoint=btrfs_data.mountpoint, metadata_level=btrfs_data.metaDataLevel, data_level=btrfs_data.dataLevel, parents=members, create_options=btrfs_data.mkfsopts ) storage.create_device(request)
def _execute_partition_data(self, storage, data, partition_data): """Execute the partition data. :param storage: an instance of the Blivet's storage object :param data: an instance of kickstart data :param partition_data: an instance of PartData """ devicetree = storage.devicetree kwargs = {} if partition_data.onbiosdisk != "": # edd_dict is only modified during storage.reset(), so don't do that # while executing storage. for (disk, biosdisk) in storage.edd_dict.items(): if "%x" % biosdisk == partition_data.onbiosdisk: partition_data.disk = disk break if not partition_data.disk: raise StorageError( _("No disk found for specified BIOS disk \"{}\".").format( partition_data.onbiosdisk ) ) size = None if partition_data.mountpoint == "swap": ty = "swap" partition_data.mountpoint = "" if partition_data.recommended or partition_data.hibernation: disk_space = self._disk_free_space size = suggest_swap_size( hibernation=partition_data.hibernation, disk_space=disk_space ) partition_data.grow = False # if people want to specify no mountpoint for some reason, let them # this is really needed for pSeries boot partitions :( elif partition_data.mountpoint == "None": partition_data.mountpoint = "" if partition_data.fstype: ty = partition_data.fstype else: ty = storage.default_fstype elif partition_data.mountpoint == 'appleboot': ty = "appleboot" partition_data.mountpoint = "" elif partition_data.mountpoint == 'prepboot': ty = "prepboot" partition_data.mountpoint = "" elif partition_data.mountpoint == 'biosboot': ty = "biosboot" partition_data.mountpoint = "" elif partition_data.mountpoint.startswith("raid."): ty = "mdmember" kwargs["name"] = partition_data.mountpoint partition_data.mountpoint = "" if devicetree.get_device_by_name(kwargs["name"]): raise StorageError( _("RAID partition \"{}\" is defined multiple times.").format(kwargs["name"]) ) if partition_data.onPart: data.onPart[kwargs["name"]] = partition_data.onPart elif partition_data.mountpoint.startswith("pv."): ty = "lvmpv" kwargs["name"] = partition_data.mountpoint partition_data.mountpoint = "" if devicetree.get_device_by_name(kwargs["name"]): raise StorageError( _("PV partition \"{}\" is defined multiple times.").format(kwargs["name"]) ) if partition_data.onPart: data.onPart[kwargs["name"]] = partition_data.onPart elif partition_data.mountpoint.startswith("btrfs."): ty = "btrfs" kwargs["name"] = partition_data.mountpoint partition_data.mountpoint = "" if devicetree.get_device_by_name(kwargs["name"]): raise StorageError( _("Btrfs partition \"{}\" is defined multiple times.").format(kwargs["name"]) ) if partition_data.onPart: data.onPart[kwargs["name"]] = partition_data.onPart elif partition_data.mountpoint == "/boot/efi": if blivet.arch.is_mactel(): ty = "macefi" else: ty = "EFI System Partition" partition_data.fsopts = "defaults,uid=0,gid=0,umask=077,shortname=winnt" else: if partition_data.fstype != "": ty = partition_data.fstype elif partition_data.mountpoint == "/boot": ty = storage.default_boot_fstype else: ty = storage.default_fstype if not size and partition_data.size: size = self._get_size(partition_data.size, "MiB") # If this specified an existing request that we should not format, # quit here after setting up enough information to mount it later. if not partition_data.format: if not partition_data.onPart: raise StorageError(_("part --noformat must also use the --onpart option.")) dev = devicetree.resolve_device(partition_data.onPart) if not dev: raise StorageError( _("Partition \"{}\" given in part command does " "not exist.").format(partition_data.onPart) ) if partition_data.resize: size = dev.raw_device.align_target_size(size) if size < dev.currentSize: # shrink try: devicetree.actions.add(ActionResizeFormat(dev, size)) devicetree.actions.add(ActionResizeDevice(dev, size)) except ValueError as e: self._handle_invalid_target_size(e, partition_data.size, dev.name) else: # grow try: devicetree.actions.add(ActionResizeDevice(dev, size)) devicetree.actions.add(ActionResizeFormat(dev, size)) except ValueError as e: self._handle_invalid_target_size(e, partition_data.size, dev.name) dev.format.mountpoint = partition_data.mountpoint dev.format.mountopts = partition_data.fsopts if ty == "swap": storage.add_fstab_swap(dev) return # Now get a format to hold a lot of these extra values. kwargs["fmt"] = get_format(ty, mountpoint=partition_data.mountpoint, label=partition_data.label, fsprofile=partition_data.fsprofile, mountopts=partition_data.fsopts, create_options=partition_data.mkfsopts, size=size) if not kwargs["fmt"].type: raise StorageError( _("The \"{}\" file system type is not supported.").format(ty) ) # If we were given a specific disk to create the partition on, verify # that it exists first. If it doesn't exist, see if it exists with # mapper/ on the front. If that doesn't exist either, it's an error. if partition_data.disk: disk = devicetree.resolve_device(partition_data.disk) # if this is a multipath member promote it to the real mpath if disk and disk.format.type == "multipath_member": mpath_device = disk.children[0] log.info("kickstart: part: promoting %s to %s", disk.name, mpath_device.name) disk = mpath_device if not disk: raise StorageError( _("Disk \"{}\" given in part command does " "not exist.").format(partition_data.disk) ) if not disk.partitionable: raise StorageError( _("Cannot install to unpartitionable device " "\"{}\".").format(partition_data.disk) ) if disk and disk.partitioned: kwargs["parents"] = [disk] elif disk: raise StorageError( _("Disk \"{}\" in part command is not " "partitioned.").format(partition_data.disk) ) if not kwargs["parents"]: raise StorageError( _("Disk \"{}\" given in part command does " "not exist.").format(partition_data.disk) ) kwargs["grow"] = partition_data.grow kwargs["size"] = size if partition_data.maxSizeMB: maxsize = self._get_size(partition_data.maxSizeMB, "MiB") else: maxsize = None kwargs["maxsize"] = maxsize kwargs["primary"] = partition_data.primOnly add_fstab_swap = None # If we were given a pre-existing partition to create a filesystem on, # we need to verify it exists and then schedule a new format action to # take place there. Also, we only support a subset of all the options # on pre-existing partitions. if partition_data.onPart: device = devicetree.resolve_device(partition_data.onPart) if not device: raise StorageError( _("Partition \"{}\" given in part command does " "not exist.").format(partition_data.onPart) ) storage.devicetree.recursive_remove(device, remove_device=False) if partition_data.resize: size = device.raw_device.align_target_size(size) try: devicetree.actions.add(ActionResizeDevice(device, size)) except ValueError as e: self._handle_invalid_target_size(e, partition_data.size, device.name) devicetree.actions.add(ActionCreateFormat(device, kwargs["fmt"])) if ty == "swap": add_fstab_swap = device # tmpfs mounts are not disks and don't occupy a disk partition, # so handle them here elif partition_data.fstype == "tmpfs": request = storage.new_tmp_fs(**kwargs) storage.create_device(request) else: # If a previous device has claimed this mount point, delete the # old one. try: if partition_data.mountpoint: device = storage.mountpoints[partition_data.mountpoint] storage.destroy_device(device) except KeyError: pass request = storage.new_partition(**kwargs) storage.create_device(request) if ty == "swap": add_fstab_swap = request if partition_data.encrypted: passphrase = self._get_passphrase(partition_data) cert = storage.get_escrow_certificate(partition_data.escrowcert) # Get the version of LUKS and PBKDF arguments. partition_data.luks_version = (partition_data.luks_version or storage.default_luks_version) pbkdf_args = get_pbkdf_args( luks_version=partition_data.luks_version, pbkdf_type=partition_data.pbkdf, max_memory_kb=partition_data.pbkdf_memory, iterations=partition_data.pbkdf_iterations, time_ms=partition_data.pbkdf_time ) if pbkdf_args and not luks_data.pbkdf_args: luks_data.pbkdf_args = pbkdf_args if partition_data.onPart: luksformat = kwargs["fmt"] device.format = get_format( "luks", passphrase=passphrase, device=device.path, cipher=partition_data.cipher, escrow_cert=cert, add_backup_passphrase=partition_data.backuppassphrase, luks_version=partition_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice( "luks%d" % storage.next_id, fmt=luksformat, parents=device ) else: luksformat = request.format request.format = get_format( "luks", passphrase=passphrase, cipher=partition_data.cipher, escrow_cert=cert, add_backup_passphrase=partition_data.backuppassphrase, luks_version=partition_data.luks_version, pbkdf_args=pbkdf_args ) luksdev = LUKSDevice("luks%d" % storage.next_id, fmt=luksformat, parents=request) if ty == "swap": # swap is on the LUKS device not on the LUKS' parent device, # override the info here add_fstab_swap = luksdev storage.create_device(luksdev) if add_fstab_swap: storage.add_fstab_swap(add_fstab_swap)
def _check_mount_point(self, mount_point): """Check if the given mount point is valid.""" if mount_point != "" and mount_point[0] != '/': msg = _("The mount point \"{}\" is not valid. It must start with a /.") raise StorageError(msg.format(mount_point))
def _handle_invalid_target_size(self, exception, size, device): """Handle an invalid target size.""" msg = _("Target size \"{size}\" for device \"{device}\" is invalid.") raise StorageError(msg.format(size=size, device=device)) from exception