Skip to content

Commit a353933

Browse files
committed
service: Infer boot device for reused LVM and RAID
1 parent 15594b3 commit a353933

File tree

4 files changed

+851
-18
lines changed

4 files changed

+851
-18
lines changed

service/lib/agama/storage/config_solvers/boot.rb

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ def solve(config)
4848
# @return [Storage::System]
4949
attr_reader :storage_system
5050

51-
# Finds a device for booting and sets its alias, if needed.
51+
# Finds a device for booting.
52+
#
53+
# It there is already an entry pointing to that device, it may set an alias for that config
54+
# entry if needed.
55+
#
56+
# If there is no Drive or MdRaid entry, it may add it to the config.
5257
#
5358
# A boot device cannot be automatically inferred in the following scenarios:
5459
# * The root partition or logical volume is missing.
@@ -69,12 +74,15 @@ def solve_device_alias
6974

7075
# Config of the device used for allocating root, directly or indirectly.
7176
#
72-
# The boot device has to be a partitioned drive. If root is not directly created as a
73-
# partition of a drive (e.g., as logical volume, as partition of a MD RAID, etc), then the
74-
# first partitioned drive used for allocating the device (physical volume or MD member
75-
# device) is considered as boot device.
77+
# The boot device has to be a partitioned drive or hardware RAID. If root is not directly
78+
# created as a partition of a drive (e.g., as logical volume, as partition of a MD RAID,
79+
# etc), then the first partitioned drive used for allocating the device (physical volume
80+
# or MD member device) is considered as boot device.
81+
#
82+
# The boot device is recursively searched until reaching a drive or a hardware RAID.
7683
#
77-
# The boot device is recursively searched until reaching a drive.
84+
# For reused LVMs or RAIDs, the result may be a new config entry created to point to the
85+
# appropriate boot device.
7886
#
7987
# @return [Configs::Drive, Configs::MdRaid, nil] nil if the boot device cannot be inferred
8088
# from the config.
@@ -130,14 +138,14 @@ def partitionable_from_md_raid(md_raid)
130138

131139
# Recursively looks for the first partitioned config from the given MD RAID.
132140
#
141+
# If no config is found, it may create and return a new Drive or MdRaid config.
142+
#
133143
# @param md_raid [Configs::MdRaid]
134144
# @return [Configs::Drive, Configs::MdRaid, nil]
135145
def partitionable_from_found_md_raid(md_raid)
136146
return md_raid if storage_system.candidate?(md_raid.found_device)
137147

138-
# TODO: find the correct underlying disk devices for the MD RAID (note they may lack
139-
# a corresponding drive entry at the configuration)
140-
nil
148+
partitionable_from_found(md_raid.found_device)
141149
end
142150

143151
# Recursively looks for the first partitioned drive from the given MD RAID.
@@ -151,9 +159,20 @@ def partitioned_drive_from_new_md_raid(md_raid)
151159

152160
# Recursively looks for the first partitioned config from the given volume group.
153161
#
162+
# If no config is found, it may create and return a new Drive or MdRaid config.
163+
#
154164
# @param volume_group [Configs::VolumeGroup]
155165
# @return [Configs::Drive, Configs::MdRaid, nil]
156166
def partitionable_from_volume_group(volume_group)
167+
partitionable_from_volume_group_pvs(volume_group) ||
168+
(volume_group.found_device && partitionable_from_found(volume_group.found_device))
169+
end
170+
171+
# Recursively looks for the first partitioned config from the given volume group.
172+
#
173+
# @param volume_group [Configs::VolumeGroup]
174+
# @return [Configs::Drive, Configs::MdRaid, nil]
175+
def partitionable_from_volume_group_pvs(volume_group)
157176
pv_devices = find_devices(volume_group.physical_volumes_devices, is_target: true)
158177
pvs = find_devices(volume_group.physical_volumes)
159178

@@ -212,6 +231,94 @@ def find_md_raid(device_alias)
212231
def find_volume_group(device_alias)
213232
config.volume_groups.find { |v| v.logical_volume?(device_alias) }
214233
end
234+
235+
# Finds or creates a config pointing to the bootable device corresponding to the given
236+
# RAID or volume group.
237+
#
238+
# @param device [Y2Storage::Md, Y2Storage::LvmVg]
239+
# @return [Configs::Drive, Configs::MdRaid, nil]
240+
def partitionable_from_found(device)
241+
disks = bootable_devices(device)
242+
return if disks.empty?
243+
244+
config_entry(disks)
245+
end
246+
247+
# Finds all devices that could be used to boot into the given RAID or volume group
248+
#
249+
# @see #partitionable_from_found
250+
#
251+
# @param device [Y2Storage::Md, Y2Storage::LvmVg]
252+
# @return [Array<Y2Storage::Partitionable>]
253+
def bootable_devices(device)
254+
device.ancestors.select do |dev|
255+
dev.is?(:disk_device) && dev.partition_table? && storage_system.candidate?(dev)
256+
end
257+
end
258+
259+
# @see #partitionable_from_found
260+
#
261+
# @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices
262+
# @return [Configs::Drive, Configs::MdRaid]
263+
def config_entry(devices)
264+
find_config_entry(devices) || create_config_entry(devices)
265+
end
266+
267+
# Find the first entry in the current configuration that corresponds to any of the given
268+
# devices
269+
#
270+
# @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices
271+
# @return [Configs::Drive, Configs::MdRaid, nil]
272+
def find_config_entry(devices)
273+
sids = devices.map(&:sid)
274+
raid = config.md_raids.find { |d| sids.include?(d.found_device&.sid) }
275+
return raid if raid
276+
277+
config.drives.find { |d| sids.include?(d.found_device.sid) }
278+
end
279+
280+
# Creates a new entry in the config to point to one of the given devices
281+
#
282+
# @param devices [Array<Y2Storage::Partitionable>] list of candidate RAIDs or disk devices
283+
# @return [Configs::Drive, Configs::MdRaid]
284+
def create_config_entry(devices)
285+
device = preferred_device_to_create_entry(devices)
286+
device.is?(:raid) ? create_raid_entry(device) : create_drive_entry(device)
287+
end
288+
289+
# @see #create_config_entry
290+
#
291+
# @param devices [Array<Y2Storage::Partitionable>]
292+
# @return [Y2Storage::Partitionable]
293+
def preferred_device_to_create_entry(devices)
294+
devices = devices.select { |d| d.is?(:raid) } if devices.any? { |d| d.is?(:raid) }
295+
devices.min_by(&:name)
296+
end
297+
298+
# @see #create_config_entry
299+
#
300+
# @param device [<Y2Storage::Partitionable>] disk device
301+
# @return [Configs::Drive]
302+
def create_drive_entry(device)
303+
config.drives << Configs::Drive.new.tap do |drive|
304+
drive.search.name = device.name
305+
drive.search.solve(device)
306+
end
307+
config.drives.last
308+
end
309+
310+
# @see #create_config_entry
311+
#
312+
# @param device [<Y2Storage::Md>] RAID device
313+
# @return [Configs::MdRaid]
314+
def create_raid_entry(device)
315+
config.md_raids << Configs::MdRaid.new.tap do |md|
316+
md.search = Configs::Search.new
317+
md.search.name = device.name
318+
md.search.solve(device)
319+
end
320+
config.md_raids.last
321+
end
215322
end
216323
end
217324
end

0 commit comments

Comments
 (0)