/
boot_requirements_checker.rb
187 lines (164 loc) · 5.46 KB
/
boot_requirements_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
# encoding: utf-8
# Copyright (c) [2015] 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 "y2storage/boot_requirements_strategies"
require "y2storage/storage_manager"
module Y2Storage
#
# Class that can check requirements for the different kinds of boot
# partition: /boot, EFI-boot, PReP.
#
# See also
# https://github.com/yast/yast-bootloader/blob/master/SUPPORTED_SCENARIOS.md
#
class BootRequirementsChecker
include Yast::Logger
class Error < RuntimeError
end
# Constructor
#
# @see #devicegraph
# @see #planned_devices
# @see #boot_disk_name
def initialize(devicegraph, planned_devices: [], boot_disk_name: nil)
@devicegraph = devicegraph
@planned_devices = planned_devices
@boot_disk_name = boot_disk_name
end
# Partitions needed in order to be able to boot the system
#
# So far, this method is used by {GuidedProposal} and by {AutoinstProposal},
# since both need to know which partitions they should create.
#
# At the moment of writing, this is not used directly by {SetupChecker}
# (the mechanism used by the Partitioner to find and report wrong setups).
# See {#errors} and {#warnings} for that.
#
# @raise [BootRequirementsChecker::Error] if adding partitions is not
# enough to make the system bootable
#
# @param target [Symbol] :desired means the sizes of the partitions should
# be the ideal ones, :min for generating the smallest functional partitions
#
# @return [Array<Planned::Partition>]
def needed_partitions(target = :desired)
strategy.needed_partitions(target)
rescue BootRequirementsStrategies::Error => error
raise Error, error.message
end
# Whether the current setup contains all necessary devices for booting
#
# @return [Boolean]
def valid?
errors.empty? && warnings.empty?
end
# All boot warnings detected in the setup, for example, when size of a /boot/efi partition
# is out of borders.
#
# So far, this is mainly used by {SetupError} (which is the mechanism used
# by the partitioner). The proposals rely on {#needed_partitions} instead.
#
# @return [Array<SetupError>]
def warnings
strategy.warnings
end
# All fatal boot errors detected in the setup, for example, when a /boot/efi partition
# is missing in a UEFI system
#
# So far, this is mainly used by {SetupError} (which is the mechanism used
# by the partitioner). The proposals rely on {#needed_partitions} instead.
#
# @return [Array<SetupError>]
def errors
strategy.errors
end
protected
# @return [Devicegraph] starting situation.
attr_reader :devicegraph
# @return [Array<Planned::Device>] devices that are already planned to be
# added to the starting devicegraph.
attr_reader :planned_devices
# @return [String, nil] device name of the disk that the system will try to
# boot first. Only useful in some scenarios like legacy boot.
# See {BootRequirementsStrategies::Analyzer#boot_disk}.
attr_reader :boot_disk_name
def arch
@arch ||= StorageManager.instance.arch
end
def strategy
@strategy ||= strategy_class.new(devicegraph, planned_devices, boot_disk_name)
end
# @see #strategy
#
# @return [BootRequirementsStrategies::Base]
def strategy_class
if nfs_root?
BootRequirementsStrategies::NfsRoot
elsif raspberry_pi?
BootRequirementsStrategies::Raspi
else
arch_strategy_class
end
end
# @see #strategy
#
# @return [BootRequirementsStrategies::Base]
def arch_strategy_class
if arch.efiboot?
BootRequirementsStrategies::UEFI
elsif arch.s390?
BootRequirementsStrategies::ZIPL
elsif arch.ppc?
BootRequirementsStrategies::PReP
else
# Fallback to Legacy as default
BootRequirementsStrategies::Legacy
end
end
# Whether the root filesystem is NFS
#
# @return [Boolean]
def nfs_root?
planned_nfs_root? || exist_nfs_root?
end
# Whether there is a planned NFS root
#
# @return [Boolean]
def planned_nfs_root?
planned_devices.find { |d| d.is_a?(Planned::Nfs) && d.mount_point == "/" }
end
# Whether a NFS root already exists
#
# @return [Boolean]
def exist_nfs_root?
devicegraph.nfs_mounts.any? { |i| i.mount_point && i.mount_point.root? }
end
# @see #raspberry_pi?
VENDOR_MODEL_PATH = "/proc/device-tree/model"
private_constant :VENDOR_MODEL_PATH
# Whether this is a Raspberry Pi. See fate#323484
#
# @return [Boolean]
def raspberry_pi?
return false unless File.exist?(VENDOR_MODEL_PATH)
File.read(VENDOR_MODEL_PATH).match?(/Raspberry Pi/i)
end
end
end