-
Notifications
You must be signed in to change notification settings - Fork 19
/
autoinst_drives_map.rb
254 lines (229 loc) · 8.9 KB
/
autoinst_drives_map.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#!/usr/bin/env ruby
#
# encoding: utf-8
# Copyright (c) [2017] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty 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, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
require "y2storage/exceptions"
module Y2Storage
module Proposal
# Utility class to map disk names to the <drive> section of the AutoYaST
# profile that will be applied to that disk.
#
# @see AutoinstProfile::PartitioningSection
# @see AutoinstProfile::DriveSection
class AutoinstDrivesMap
extend Forwardable
# @!method each_pair
# Calls block once por each disk that contains the AutoYaST specification
# passing as arguments the disk name and the corresponding
# AutoinstProfile::DriveSection object.
#
# @example
# drives_map.each_pair do |disk_name, drive_section|
# puts "Drive for #{disk_name}: #{drive_section}"
# end
#
# @!method each
# @see #each_pair
def_delegators :@drives, :each, :each_pair
# @return [AutoinstIssues::List] List of found AutoYaST issues
attr_reader :issues_list
# Constructor
#
# @param devicegraph [Devicegraph] Devicegraph where the disks are contained
# @param partitioning [AutoinstProfile::PartitioningSection] Partitioning layout
# from an AutoYaST profile
# @param issues_list [AutoinstIssues::List] List of AutoYaST issues to register them
def initialize(devicegraph, partitioning, issues_list)
@drives = {}
@issues_list = issues_list
add_disks(partitioning.disk_drives, devicegraph)
add_vgs(partitioning.lvm_drives)
add_mds(partitioning.md_drives)
end
# Returns the list of disk names
#
# @return [Array<String>] Disk names
def disk_names
@drives.keys
end
# Returns whether the map contains partitions
#
# @example Containing partitions
# devicegraph = Y2Storage::StorageManager.instance.probed
# array = [
# {
# "device" => "/dev/sda", "use" => "all", "partitions" => [
# { "mount" => "/" }
# ]
# }
# ]
# profile = AutoinstProfile::PartitioningSection.new_from_hashes(array)
# map = AutoinstDriveMap.new(devicegraph, profile)
# map.partitions? # => true
#
# @example Not containing partitions
# devicegraph = Y2Storage::StorageManager.instance.probed
# array = [{ "device" => "/dev/sda", "use" => "all" }]
# profile = AutoinstProfile::PartitioningSection.new_from_hashes(array)
# map = AutoinstDriveMap.new(devicegraph, profile)
# map.partitions? # => false
def partitions?
@drives.values.any? { |i| !i.partitions.empty? }
end
# Determine whether any of the drives sets snapshots enabled
#
# @return [Boolean]
def use_snapshots?
@drives.empty? || @drives.values.any? do |drive|
drive.enable_snapshots.nil? || drive.enable_snapshots
end
end
protected
# Find the first usable disk for the given <drive> AutoYaST specification
#
# @param drive [AutoinstProfile::DriveSection] AutoYaST drive specification
# @param devicegraph [Devicegraph] Devicegraph
# @return [Disk,nil] Usable disk or nil if none is found
def first_usable_disk(drive, devicegraph)
skip_list = drive.skip_list
devicegraph.disk_devices.each do |disk|
next if disk_names.include?(disk.name)
next if skip_list.matches?(disk)
return disk
end
nil
end
# Adds disks to the devices map
#
# If some disk does not specify a "device" property, an usable disk will
# be chosen from the given devicegraph.
#
# @param disks [Array<AutoinstProfile::DriveSection>] List of disk specifications from AutoYaST
# @param devicegraph [Devicegraph] Devicegraph to search for disks for "flexible" devices
def add_disks(disks, devicegraph)
fixed_drives, flexible_drives = disks.partition(&:device)
fixed_drives.each do |drive|
disk = find_disk(devicegraph, drive.device)
if disk
@drives[disk.name] = drive
elsif stray_devices_group?(drive, devicegraph)
@drives[drive.device] = drive
else
issues_list.add(:no_disk, drive)
end
end
flexible_drives.each do |drive|
disk = first_usable_disk(drive, devicegraph)
if disk.nil?
issues_list.add(:no_disk, drive)
next
end
@drives[disk.name] = drive
end
end
# Adds volume groups to the devices map
#
# All volume groups should have a "device" property.
#
# @param vgs [Array<AutoinstProfile::DriveSection>] List of LVM VG specifications from AutoYaST
def add_vgs(vgs)
vgs.each { |v| @drives[v.device] = v }
end
# Adds MD arrays to the devices map
#
# @see AutoinstProfile::DriveSection#name_for_md for details about the
# logic used to infer the device name.
#
# @param mds [Array<AutoinstProfile::DriveSection>] List of MD RAID specifications from AutoYaST
def add_mds(mds)
mds.each do |md|
@drives[md.name_for_md] = md
end
end
# Find a disk using any possible name
#
# @return [Disk,nil] Usable disk or nil if none is found
# @see [Y2Storage::Devicegraph#find_by_any_name]
def find_disk(devicegraph, device_name)
device = devicegraph.find_by_any_name(device_name)
return nil unless device
([device] + device.ancestors).find { |d| d.is?(:disk_device) }
end
# Whether the given <drive> section represents a set of Xen virtual
# partitions
#
# FIXME: this is a very simplistic approach implemented as bugfix for
# bsc#1085134. A <drive> section is only considered to represent a set of
# virtual partitions if ALL its partitions contain an explicit
# partition_nr that matches with the name of a stray block device. If any
# of the <partition> subsections does not include partition_nr or does not
# match with an existing device, the whole drive is discarded.
#
# NOTE: in the future the AutoYaST profile will hopefully allow a more
# flexible usage of disks. Then Xen virtual partitions could be
# represented as disks in the profile (which matches reality way better),
# and there will be no need to improve this method much further.
#
# See below for an example of an AutoYaST profile for a system with
# the virtual partitions /dev/xvda1, /dev/xvda2 and /dev/xvdb1. That
# example includes two devices /dev/xvda and /dev/xvdb that really do
# not exist in the system.
#
# So this method checks if the given <drive> section contains a set of
# <partition> subsections that correspond to existing Xen virtual
# partitions (stray block devices) in the system.
#
# @example AutoYaST profile for Xen virtual partitions
# <drive>
# <device>/dev/xvda</device>
# <partition>
# <partition_nr>1</partition_nr>
# ...information about /dev/xvda1...
# </partition>
# <partition>
# <partition_nr>2</partition_nr>
# ...information about /dev/xvda2...
# </partition>
# </drive>
#
# <drive>
# <device>/dev/xvdb</device>
# <partition>
# <partition_nr>1</partition_nr>
# ...information about /dev/xvdb1...
# </partition>
# </drive>
#
# @param drive [AutoinstProfile::DriveSection] AutoYaST drive specification
# @param devicegraph [Devicegraph] Devicegraph
# @return [Boolean] false if there are no partition sections or they do
# not correspond to stray devices
def stray_devices_group?(drive, devicegraph)
return false if drive.partitions.empty?
devices = devicegraph.stray_blk_devices
drive.partitions.all? do |partition|
next false if partition.partition_nr.nil?
name = drive.device + partition.partition_nr.to_s
devices.any? { |dev| dev.name == name }
end
end
end
end
end