-
Notifications
You must be signed in to change notification settings - Fork 19
/
setup_checker.rb
241 lines (213 loc) · 7.71 KB
/
setup_checker.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
# Copyright (c) [2018-2022] 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 "yast"
require "pathname"
require "y2storage/blk_device"
require "y2storage/setup_error"
require "y2storage/boot_requirements_checker"
require "y2storage/proposal_settings"
require "y2storage/with_security_policies"
# This 'import' is necessary to load the control file (/etc/YaST/control.xml)
# when running in an installed system. During installation, this module
# is imported by WorkflowManager.
Yast.import "ProductControl"
Yast.import "Mode"
module Y2Storage
# Class to check whether a setup (devicegraph) fulfills the storage requirements
#
# The storage requirements are the set of necessary volumes to properly boot and
# run the system. Boot checkings are actually performed by class BootRequirementsChecker,
# and needed volumes to run the system are read from control file (control.xml).
#
# @example
# checker = SetupChecker.new(devicegraph)
# checker.valid? #=> true
class SetupChecker
include Yast::I18n
include Yast::Logger
include WithSecurityPolicies
# @return [Devicegraph]
attr_reader :devicegraph
# Constructor
#
# @param devicegraph [Devicegraph] setup to check
def initialize(devicegraph)
textdomain "storage"
@devicegraph = devicegraph
end
# Whether the current setup fulfills the storage requirements
#
# @return [Boolean]
def valid?
errors.empty? && warnings.empty?
end
# Whether the system contain fatal errors preventing to continue
#
# @return [Array<SetupError>]
def errors
boot_requirements_checker.errors
end
# All storage warnings detected in the setup, for example, when a /boot/efi partition
# is missing in a UEFI system, or a required product volume (e.g., swap) is missing.
#
# @see SetupError
#
# @return [Array<SetupError>]
def warnings
boot_warnings + product_warnings + mount_warnings + security_policy_warnings
end
# All boot errors detected in the setup
#
# @return [Array<SetupError>]
#
# @see BootRequirementsChecker#errors
def boot_warnings
@boot_warnings ||= boot_requirements_checker.warnings
end
# All product warnings detected in the setup
#
# This checks that all mandatory volumes specified in control file are present
# in the system.
#
# @return [Array<SetupError>]
def product_warnings
@product_warnings ||= missing_product_volumes.map { |v| SetupError.new(missing_volume: v) }
end
# All warnings related to the definition of the mount points in the setup
#
# This checks for example whether the mount options make sense.
#
# @return [Array<SetupError>]
def mount_warnings
devicegraph.mount_points.map { |mp| mount_warning(mp) }.compact
end
# Security policy warnings detected in the setup
#
# @return [Array<SetupError>]
def security_policy_warnings
@security_policy_warnings ||= security_policy_failing_rules.map do |rule|
SetupError.new(message: "#{rule.identifiers.first} #{rule.description}")
end
end
# Currently enabled security policy
#
# @note yast2-security might not be available, see {#with_security_policies}.
#
# @return [Y2Security::SecurityPolicies::Policy, nil]
def security_policy
with_security_policies { Y2Security::SecurityPolicies::Manager.instance.enabled_policy }
end
# Failing rules from the enabled security policy
#
# @note yast2-security might not be available, see {#with_security_policies}.
#
# @return [Array<Y2Security::SecurityPolicies::Rule>]
def security_policy_failing_rules
return [] unless Yast::Mode.installation
failing_rules = with_security_policies do
policies_manager = Y2Security::SecurityPolicies::Manager.instance
target_config = Y2Security::SecurityPolicies::TargetConfig.new.tap do |config|
config.storage = devicegraph
end
policies_manager.failing_rules(target_config, scope: :storage)
end
failing_rules || []
end
private
# Mandatory product volumes that are not present in the current setup
#
# @see #product_volumes
#
# @return [Array<VolumeSpecification>]
def missing_product_volumes
product_volumes.select { |v| missing?(v) }
end
# Mandatory product volumes for a valid storage setup
#
# @note This volumes are obtained from the control file (control.xml) and only
# mandatory volumes are taken into account.
#
# @see ProposalSettings#volumes
#
# @return [Array<VolumeSpecification>]
def product_volumes
# ProposalSettings#volumes is initialized to nil when using old settings format
# because this attribute does not exist with old format
volumes = ProposalSettings.new_for_current_product.volumes || []
volumes.select { |v| mandatory?(v) }
end
# Whether a volume is mandatory, that is, the volume is proposed to be created
# and cannot be deactivated.
#
# @param volume [VolumeSpecification]
# @return [Boolean] true if the volume is mandatory; false otherwise.
def mandatory?(volume)
volume.proposed? && !volume.proposed_configurable?
end
# Whether a volume is missing in the current setup
#
# @see BlkDevice#match_volume?
#
# @param volume [VolumeSpecification]
# @return [Boolean] true if the volume is missing; false otherwise.
def missing?(volume)
# Users mounting system volumes as NFS are supposed to know what they are doing
return false if nfs?(volume)
BlkDevice.all(devicegraph).none? { |d| d.match_volume?(volume) }
end
# Whether a volume is present in the current setup as an NFS mount
#
# @param volume [VolumeSpecification]
# @return [Boolean]
def nfs?(volume)
return false unless volume.mount_point
devicegraph.nfs_mounts.any? do |nfs|
nfs.mount_point&.path?(volume.mount_point)
end
end
# @see #mount_warnings
#
# @param mount_point [MountPoint]
# @return [SetupError, nil]
def mount_warning(mount_point)
missing = mount_point.missing_mount_options
return if missing.empty?
# TRANSLATORS: do not translate %{opt} or %{path}. %{opt} is replaced by the name of a mount
# option in the singular sentence or a list of options separated by commas in the plural one.
# ${path} is replaced by the mount point.
msg = n_(
format(
"The fstab option %{opt} may be needed to properly mount %{path}.",
opt: missing.first, path: mount_point.path
),
format(
"The following fstab options may be needed to propely mount %{path}: %{opt}",
opt: missing.join(","), path: mount_point.path
),
missing.size
)
SetupError.new(message: msg)
end
# @return [BootRequirementsChecker] shortcut for boot requirements checker
# with given device graph
def boot_requirements_checker
@boot_requirements_checker ||= BootRequirementsChecker.new(devicegraph)
end
end
end