supermicro 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/supermicro/bios.rb +277 -0
- data/lib/supermicro/boot.rb +188 -55
- data/lib/supermicro/client.rb +1 -0
- data/lib/supermicro/jobs.rb +67 -39
- data/lib/supermicro/network.rb +13 -9
- data/lib/supermicro/storage.rb +179 -74
- data/lib/supermicro/system.rb +104 -14
- data/lib/supermicro/system_config.rb +2 -75
- data/lib/supermicro/tasks.rb +39 -1
- data/lib/supermicro/utility.rb +59 -23
- data/lib/supermicro/version.rb +1 -1
- data/lib/supermicro/virtual_media.rb +53 -10
- data/lib/supermicro.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4f04d1c98be296a5225792fbf6cfc9058491f92f971e8fdf80e47c513e23579
|
4
|
+
data.tar.gz: 158c91f61686c85a89bc0040157c80089ee32e2398bc58bf675d176da405a4c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 948f17fa2f9d675bd2969e420ec248c57b9fbcab76ecc06680be3782a050db3feb0c6151fadeba82593242e293fb1f70048b49b046776aea984d8bc487f6d3cc
|
7
|
+
data.tar.gz: 496257525cda3c494c7e619eee1fb25258c07382803cc5f68df9486cbe430773981ca9d2dacac08853f325abf4139884562fdfc7a77c1be9dc852d323e07b7f8
|
@@ -0,0 +1,277 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Supermicro
|
6
|
+
module Bios
|
7
|
+
# Get BIOS attributes (public method for adapter compatibility)
|
8
|
+
def bios_attributes
|
9
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1/Bios")
|
10
|
+
|
11
|
+
if response.status == 200
|
12
|
+
begin
|
13
|
+
data = JSON.parse(response.body)
|
14
|
+
{
|
15
|
+
"attributes" => data["Attributes"],
|
16
|
+
"attribute_registry" => data["AttributeRegistry"]
|
17
|
+
}
|
18
|
+
rescue JSON::ParserError
|
19
|
+
raise Error, "Failed to parse BIOS attributes response: #{response.body}"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
raise Error, "Failed to get BIOS attributes. Status code: #{response.status}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get just the attributes hash (private helper)
|
27
|
+
private
|
28
|
+
def get_bios_attributes
|
29
|
+
result = bios_attributes
|
30
|
+
result["attributes"] || {}
|
31
|
+
rescue
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
public
|
35
|
+
|
36
|
+
# Check if BIOS error prompt is disabled
|
37
|
+
# Supermicro equivalent: WaitFor_F1_IfError should be "Disabled"
|
38
|
+
def bios_error_prompt_disabled?
|
39
|
+
attrs = get_bios_attributes
|
40
|
+
|
41
|
+
if attrs.has_key?("WaitFor_F1_IfError")
|
42
|
+
return attrs["WaitFor_F1_IfError"] == "Disabled"
|
43
|
+
else
|
44
|
+
debug "WaitFor_F1_IfError attribute not found in BIOS settings", 1, :yellow
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check if HDD placeholder is enabled
|
50
|
+
# Supermicro doesn't have a direct equivalent to Dell's HddPlaceholder
|
51
|
+
# This is a no-op that returns true for compatibility
|
52
|
+
def bios_hdd_placeholder_enabled?
|
53
|
+
# Supermicro automatically handles boot device placeholders
|
54
|
+
# No explicit setting needed
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check if OS power control is enabled
|
59
|
+
# Supermicro equivalent: Check power management settings
|
60
|
+
# PowerPerformanceTuning should be "OS Controls EPB" for OS control
|
61
|
+
def bios_os_power_control_enabled?
|
62
|
+
attrs = get_bios_attributes
|
63
|
+
|
64
|
+
# Check if OS is controlling power management
|
65
|
+
if attrs.has_key?("PowerPerformanceTuning")
|
66
|
+
return attrs["PowerPerformanceTuning"] == "OS Controls EPB"
|
67
|
+
else
|
68
|
+
# If the setting doesn't exist, assume it's not configured
|
69
|
+
debug "PowerPerformanceTuning attribute not found in BIOS settings", 1, :yellow
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Set BIOS error prompt behavior
|
75
|
+
def set_bios_error_prompt(disabled: true)
|
76
|
+
value = disabled ? "Disabled" : "Enabled"
|
77
|
+
|
78
|
+
payload = {
|
79
|
+
"Attributes" => {
|
80
|
+
"WaitFor_F1_IfError" => value
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
response = authenticated_request(
|
85
|
+
:patch,
|
86
|
+
"/redfish/v1/Systems/1/Bios/SD",
|
87
|
+
body: payload.to_json,
|
88
|
+
headers: { 'Content-Type': 'application/json' }
|
89
|
+
)
|
90
|
+
|
91
|
+
if response.status.between?(200, 299)
|
92
|
+
debug "BIOS error prompt #{disabled ? 'disabled' : 'enabled'} successfully", 1, :green
|
93
|
+
true
|
94
|
+
else
|
95
|
+
debug "Failed to set BIOS error prompt. Status: #{response.status}", 0, :red
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set OS power control
|
101
|
+
def set_bios_os_power_control(enabled: true)
|
102
|
+
value = enabled ? "OS Controls EPB" : "BIOS Controls EPB"
|
103
|
+
|
104
|
+
payload = {
|
105
|
+
"Attributes" => {
|
106
|
+
"PowerPerformanceTuning" => value
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
response = authenticated_request(
|
111
|
+
:patch,
|
112
|
+
"/redfish/v1/Systems/1/Bios/SD",
|
113
|
+
body: payload.to_json,
|
114
|
+
headers: { 'Content-Type': 'application/json' }
|
115
|
+
)
|
116
|
+
|
117
|
+
if response.status.between?(200, 299)
|
118
|
+
debug "OS power control #{enabled ? 'enabled' : 'disabled'} successfully", 1, :green
|
119
|
+
true
|
120
|
+
else
|
121
|
+
debug "Failed to set OS power control. Status: #{response.status}", 0, :red
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Ensure UEFI boot mode
|
127
|
+
def ensure_uefi_boot
|
128
|
+
attrs = get_bios_attributes
|
129
|
+
|
130
|
+
# Check current boot mode
|
131
|
+
current_mode = attrs["BootModeSelect"]
|
132
|
+
|
133
|
+
if current_mode == "UEFI"
|
134
|
+
debug "System is already in UEFI boot mode", 1, :green
|
135
|
+
return true
|
136
|
+
else
|
137
|
+
debug "System is not in UEFI boot mode (current: #{current_mode}). Setting to UEFI...", 1, :yellow
|
138
|
+
|
139
|
+
# Set UEFI boot mode
|
140
|
+
payload = {
|
141
|
+
"Attributes" => {
|
142
|
+
"BootModeSelect" => "UEFI"
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
response = authenticated_request(
|
147
|
+
:patch,
|
148
|
+
"/redfish/v1/Systems/1/Bios/SD",
|
149
|
+
body: payload.to_json,
|
150
|
+
headers: { 'Content-Type': 'application/json' }
|
151
|
+
)
|
152
|
+
|
153
|
+
if response.status.between?(200, 299)
|
154
|
+
debug "UEFI boot mode configured successfully", 1, :green
|
155
|
+
debug "Note: A reboot is required for the change to take effect", 1, :yellow
|
156
|
+
return true
|
157
|
+
else
|
158
|
+
debug "Failed to set UEFI boot mode. Status: #{response.status}", 0, :red
|
159
|
+
debug "Response: #{response.body}", 2, :red
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Ensure sensible BIOS settings (Supermicro version)
|
166
|
+
def ensure_sensible_bios!(options = {})
|
167
|
+
# Check current state
|
168
|
+
if bios_error_prompt_disabled? && bios_os_power_control_enabled?
|
169
|
+
debug "BIOS settings already configured correctly", 1, :green
|
170
|
+
return { changes_made: false }
|
171
|
+
end
|
172
|
+
|
173
|
+
debug "Configuring BIOS settings...", 1, :yellow
|
174
|
+
|
175
|
+
# Build the attributes to change
|
176
|
+
attributes = {}
|
177
|
+
|
178
|
+
# Check and prepare error prompt change
|
179
|
+
if !bios_error_prompt_disabled?
|
180
|
+
debug "Will disable BIOS error prompt (F1 wait)", 1, :yellow
|
181
|
+
attributes["WaitFor_F1_IfError"] = "Disabled"
|
182
|
+
end
|
183
|
+
|
184
|
+
# Check and prepare OS power control change
|
185
|
+
if !bios_os_power_control_enabled?
|
186
|
+
debug "Will enable OS power control", 1, :yellow
|
187
|
+
attributes["PowerPerformanceTuning"] = "OS Controls EPB"
|
188
|
+
end
|
189
|
+
|
190
|
+
# Ensure UEFI boot mode
|
191
|
+
attributes["BootModeSelect"] = "UEFI"
|
192
|
+
|
193
|
+
# Apply all changes at once
|
194
|
+
payload = { "Attributes" => attributes }
|
195
|
+
|
196
|
+
response = authenticated_request(
|
197
|
+
:patch,
|
198
|
+
"/redfish/v1/Systems/1/Bios/SD",
|
199
|
+
body: payload.to_json,
|
200
|
+
headers: { 'Content-Type': 'application/json' }
|
201
|
+
)
|
202
|
+
|
203
|
+
if response.status.between?(200, 299)
|
204
|
+
debug "BIOS settings configured successfully", 1, :green
|
205
|
+
|
206
|
+
# Check if a reboot job was created
|
207
|
+
if response.headers['Location']
|
208
|
+
debug "BIOS change job created: #{response.headers['Location']}", 1, :yellow
|
209
|
+
end
|
210
|
+
|
211
|
+
return { changes_made: true }
|
212
|
+
else
|
213
|
+
debug "Failed to apply BIOS settings. Status: #{response.status}", 0, :red
|
214
|
+
debug "Response: #{response.body}", 2, :red
|
215
|
+
return { changes_made: false, error: "Failed to apply BIOS settings" }
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Set individual BIOS attribute
|
220
|
+
def set_bios_attribute(attribute_name, value)
|
221
|
+
debug "Setting BIOS attribute #{attribute_name} to #{value}...", 1, :yellow
|
222
|
+
|
223
|
+
body = {
|
224
|
+
"Attributes" => {
|
225
|
+
attribute_name => value
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
response = authenticated_request(
|
230
|
+
:patch,
|
231
|
+
"/redfish/v1/Systems/1/Bios/SD",
|
232
|
+
body: body.to_json,
|
233
|
+
headers: { 'Content-Type': 'application/json' }
|
234
|
+
)
|
235
|
+
|
236
|
+
if response.status.between?(200, 299)
|
237
|
+
debug "BIOS attribute set successfully. Changes will be applied on next reboot.", 1, :green
|
238
|
+
return true
|
239
|
+
else
|
240
|
+
raise Error, "Failed to set BIOS attribute: #{response.status} - #{response.body}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def pending_bios_settings
|
245
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1/Bios/SD")
|
246
|
+
|
247
|
+
if response.status == 200
|
248
|
+
begin
|
249
|
+
data = JSON.parse(response.body)
|
250
|
+
data["Attributes"] || {}
|
251
|
+
rescue JSON::ParserError
|
252
|
+
raise Error, "Failed to parse pending BIOS settings response: #{response.body}"
|
253
|
+
end
|
254
|
+
else
|
255
|
+
{}
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def reset_bios_defaults
|
260
|
+
debug "Resetting BIOS to defaults...", 1, :yellow
|
261
|
+
|
262
|
+
response = authenticated_request(
|
263
|
+
:post,
|
264
|
+
"/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios",
|
265
|
+
body: {}.to_json,
|
266
|
+
headers: { 'Content-Type': 'application/json' }
|
267
|
+
)
|
268
|
+
|
269
|
+
if response.status.between?(200, 299)
|
270
|
+
debug "BIOS reset to defaults successfully. Changes will be applied on next reboot.", 1, :green
|
271
|
+
return true
|
272
|
+
else
|
273
|
+
raise Error, "Failed to reset BIOS: #{response.status} - #{response.body}"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
data/lib/supermicro/boot.rb
CHANGED
@@ -5,63 +5,100 @@ require 'colorize'
|
|
5
5
|
|
6
6
|
module Supermicro
|
7
7
|
module Boot
|
8
|
-
|
8
|
+
# Get boot configuration with snake_case fields
|
9
|
+
def boot_config
|
9
10
|
response = authenticated_request(:get, "/redfish/v1/Systems/1")
|
10
11
|
|
11
12
|
if response.status == 200
|
12
13
|
begin
|
13
14
|
data = JSON.parse(response.body)
|
15
|
+
boot_data = data["Boot"] || {}
|
16
|
+
|
17
|
+
# Get boot options for resolving references
|
18
|
+
options_map = {}
|
19
|
+
begin
|
20
|
+
options = boot_options
|
21
|
+
options.each do |opt|
|
22
|
+
options_map[opt["id"]] = opt["display_name"] || opt["name"]
|
23
|
+
end
|
24
|
+
rescue
|
25
|
+
# Ignore errors fetching boot options
|
26
|
+
end
|
14
27
|
|
15
|
-
|
28
|
+
# Build boot order with resolved names
|
29
|
+
boot_order = (boot_data["BootOrder"] || []).map do |ref|
|
30
|
+
{
|
31
|
+
"reference" => ref,
|
32
|
+
"name" => options_map[ref] || ref
|
33
|
+
}
|
34
|
+
end
|
16
35
|
|
36
|
+
# Return hash with snake_case fields
|
17
37
|
{
|
18
|
-
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"
|
22
|
-
"
|
23
|
-
|
24
|
-
|
25
|
-
|
38
|
+
# Boot override settings (for one-time or continuous boot)
|
39
|
+
"boot_source_override_enabled" => boot_data["BootSourceOverrideEnabled"], # Disabled/Once/Continuous
|
40
|
+
"boot_source_override_target" => boot_data["BootSourceOverrideTarget"], # None/Pxe/Hdd/Cd/etc
|
41
|
+
"boot_source_override_mode" => boot_data["BootSourceOverrideMode"], # UEFI/Legacy
|
42
|
+
"allowed_override_targets" => boot_data["BootSourceOverrideTarget@Redfish.AllowableValues"] || [],
|
43
|
+
|
44
|
+
# Permanent boot order with resolved names
|
45
|
+
"boot_order" => boot_order, # [{reference: "Boot0002", name: "PXE IPv4"}]
|
46
|
+
"boot_order_refs" => boot_data["BootOrder"] || [], # Raw references for set_boot_order
|
47
|
+
|
48
|
+
# Supermicro specific fields
|
49
|
+
"boot_next" => boot_data["BootNext"],
|
50
|
+
"http_boot_uri" => boot_data["HttpBootUri"],
|
51
|
+
|
52
|
+
# References to other resources
|
53
|
+
"boot_options_uri" => boot_data.dig("BootOptions", "@odata.id")
|
54
|
+
}.compact
|
26
55
|
rescue JSON::ParserError
|
27
|
-
raise Error, "Failed to parse boot
|
56
|
+
raise Error, "Failed to parse boot response: #{response.body}"
|
28
57
|
end
|
29
58
|
else
|
30
|
-
raise Error, "Failed to get boot
|
59
|
+
raise Error, "Failed to get boot configuration. Status code: #{response.status}"
|
31
60
|
end
|
32
61
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
62
|
+
|
63
|
+
# Get raw Redfish boot data (CamelCase)
|
64
|
+
def boot_raw
|
65
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1")
|
36
66
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
67
|
+
if response.status == 200
|
68
|
+
data = JSON.parse(response.body)
|
69
|
+
data["Boot"] || {}
|
70
|
+
else
|
71
|
+
raise Error, "Failed to get boot configuration. Status code: #{response.status}"
|
41
72
|
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Shorter alias for convenience
|
76
|
+
def boot
|
77
|
+
boot_config
|
78
|
+
end
|
79
|
+
|
80
|
+
# Set boot override for next boot
|
81
|
+
def set_boot_override(target, enabled: "Once", mode: nil)
|
82
|
+
# Validate target against allowed values
|
83
|
+
boot_data = boot
|
84
|
+
valid_targets = boot_data["allowed_override_targets"]
|
42
85
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
elsif persistent
|
47
|
-
"Continuous" # Legacy support
|
48
|
-
else
|
49
|
-
"Once" # Default
|
86
|
+
if valid_targets && !valid_targets.include?(target)
|
87
|
+
debug "Invalid boot target '#{target}'. Allowed values: #{valid_targets.join(', ')}"
|
88
|
+
raise Error, "Invalid boot target: #{target}"
|
50
89
|
end
|
51
90
|
|
52
91
|
debug "Setting boot override to #{target} (#{enabled})..."
|
53
92
|
|
54
93
|
body = {
|
55
94
|
"Boot" => {
|
56
|
-
"BootSourceOverrideEnabled" => enabled,
|
57
|
-
"BootSourceOverrideTarget" => target
|
95
|
+
"BootSourceOverrideEnabled" => enabled, # Disabled/Once/Continuous
|
96
|
+
"BootSourceOverrideTarget" => target # None/Pxe/Hdd/Cd/etc
|
58
97
|
}
|
59
98
|
}
|
60
99
|
|
61
100
|
# Add boot mode if specified
|
62
|
-
if mode
|
63
|
-
body["Boot"]["BootSourceOverrideMode"] = mode
|
64
|
-
end
|
101
|
+
body["Boot"]["BootSourceOverrideMode"] = mode if mode
|
65
102
|
|
66
103
|
response = authenticated_request(
|
67
104
|
:patch,
|
@@ -156,51 +193,147 @@ module Supermicro
|
|
156
193
|
end
|
157
194
|
end
|
158
195
|
|
159
|
-
|
160
|
-
|
196
|
+
# Get boot options collection (detailed boot devices) with snake_case
|
197
|
+
def boot_options
|
198
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1/BootOptions")
|
161
199
|
|
162
200
|
if response.status == 200
|
163
201
|
begin
|
164
202
|
data = JSON.parse(response.body)
|
203
|
+
options = []
|
165
204
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
205
|
+
# Supermicro doesn't support $expand, fetch each individually
|
206
|
+
data["Members"]&.each do |member|
|
207
|
+
if member["@odata.id"]
|
208
|
+
opt_response = authenticated_request(:get, member["@odata.id"])
|
209
|
+
if opt_response.status == 200
|
210
|
+
opt_data = JSON.parse(opt_response.body)
|
211
|
+
options << {
|
212
|
+
"id" => opt_data["Id"], # Boot0002
|
213
|
+
"boot_option_reference" => opt_data["BootOptionReference"], # Boot0002
|
214
|
+
"display_name" => opt_data["DisplayName"], # "UEFI PXE IPv4: Intel..."
|
215
|
+
"name" => opt_data["DisplayName"] || opt_data["Name"], # Alias for display_name
|
216
|
+
"enabled" => opt_data["BootOptionEnabled"], # true/false
|
217
|
+
"uefi_device_path" => opt_data["UefiDevicePath"], # UEFI device path if present
|
218
|
+
"description" => opt_data["Description"]
|
219
|
+
}.compact
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
176
223
|
|
177
|
-
|
224
|
+
options
|
178
225
|
rescue JSON::ParserError
|
179
|
-
raise Error, "Failed to parse boot
|
226
|
+
raise Error, "Failed to parse boot options response: #{response.body}"
|
180
227
|
end
|
181
228
|
else
|
182
229
|
[]
|
183
230
|
end
|
184
231
|
end
|
232
|
+
|
233
|
+
# Alias for backwards compatibility
|
234
|
+
def get_boot_devices
|
235
|
+
boot_options
|
236
|
+
end
|
185
237
|
|
186
|
-
|
187
|
-
|
238
|
+
# Convenience methods for common boot targets
|
239
|
+
def boot_to_pxe(enabled: "Once", mode: nil)
|
240
|
+
set_boot_override("Pxe", enabled: enabled, mode: mode)
|
188
241
|
end
|
189
242
|
|
190
|
-
def boot_to_disk(
|
191
|
-
set_boot_override("Hdd",
|
243
|
+
def boot_to_disk(enabled: "Once", mode: nil)
|
244
|
+
set_boot_override("Hdd", enabled: enabled, mode: mode)
|
192
245
|
end
|
193
246
|
|
194
|
-
def boot_to_cd(
|
195
|
-
|
247
|
+
def boot_to_cd(enabled: "Once", mode: "UEFI")
|
248
|
+
# Always use UEFI mode for CD boot since we're booting UEFI media
|
249
|
+
set_boot_override("Cd", enabled: enabled, mode: mode)
|
196
250
|
end
|
197
251
|
|
198
|
-
def boot_to_usb(
|
199
|
-
set_boot_override("Usb",
|
252
|
+
def boot_to_usb(enabled: "Once", mode: nil)
|
253
|
+
set_boot_override("Usb", enabled: enabled, mode: mode)
|
200
254
|
end
|
201
255
|
|
202
|
-
def boot_to_bios_setup(
|
203
|
-
set_boot_override("BiosSetup",
|
256
|
+
def boot_to_bios_setup(enabled: "Once", mode: nil)
|
257
|
+
set_boot_override("BiosSetup", enabled: enabled, mode: mode)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Set one-time boot to virtual media (CD)
|
261
|
+
def set_one_time_boot_to_virtual_media
|
262
|
+
debug "Setting one-time boot to virtual media...", 1, :yellow
|
263
|
+
|
264
|
+
# Supermicro often needs virtual media remounted to ensure it's properly recognized
|
265
|
+
# Check if virtual media is already mounted and remount if so
|
266
|
+
begin
|
267
|
+
require_relative 'virtual_media'
|
268
|
+
vm_status = virtual_media_status
|
269
|
+
|
270
|
+
if vm_status && vm_status["Inserted"]
|
271
|
+
current_image = vm_status["Image"]
|
272
|
+
if current_image
|
273
|
+
debug "Remounting virtual media to ensure fresh connection...", 1, :yellow
|
274
|
+
|
275
|
+
# Eject current media
|
276
|
+
eject_virtual_media rescue nil
|
277
|
+
sleep 2
|
278
|
+
|
279
|
+
# Re-insert the media
|
280
|
+
insert_virtual_media(current_image)
|
281
|
+
sleep 3
|
282
|
+
|
283
|
+
debug "Virtual media remounted: #{current_image}", 1, :green
|
284
|
+
end
|
285
|
+
end
|
286
|
+
rescue => e
|
287
|
+
debug "Note: Could not remount virtual media: #{e.message}", 2, :yellow
|
288
|
+
end
|
289
|
+
|
290
|
+
# Now try the standard boot override - this often works after remount
|
291
|
+
result = boot_to_cd(enabled: "Once")
|
292
|
+
|
293
|
+
if result
|
294
|
+
debug "One-time boot to virtual media configured", 1, :green
|
295
|
+
debug "System will boot from virtual CD on next restart", 1, :green
|
296
|
+
else
|
297
|
+
debug "Failed to set boot override, may need manual intervention", 1, :red
|
298
|
+
end
|
299
|
+
|
300
|
+
result
|
301
|
+
end
|
302
|
+
|
303
|
+
# Set boot order with hard drive first
|
304
|
+
def set_boot_order_hd_first
|
305
|
+
debug "Configuring system to boot from HD after OS installation...", 1, :yellow
|
306
|
+
|
307
|
+
# First check if there's actually a hard disk present
|
308
|
+
boot_opts = boot_options
|
309
|
+
hd_option = boot_opts.find { |opt|
|
310
|
+
opt["display_name"] =~ /Hard Drive|HDD|SATA|NVMe|SSD|RAID|UEFI OS/i
|
311
|
+
}
|
312
|
+
|
313
|
+
if hd_option
|
314
|
+
# HD exists, set it as first in boot order
|
315
|
+
other_options = boot_opts.select { |opt|
|
316
|
+
opt["enabled"] && opt["id"] != hd_option["id"]
|
317
|
+
}.sort_by { |opt| opt["id"] }
|
318
|
+
|
319
|
+
new_order = [hd_option["id"]] + other_options.map { |opt| opt["id"] }
|
320
|
+
debug "Setting boot order with HD first: #{new_order.join(', ')}", 2, :yellow
|
321
|
+
result = set_boot_order(new_order)
|
322
|
+
|
323
|
+
if result
|
324
|
+
debug "Boot order set with HD first", 1, :green
|
325
|
+
end
|
326
|
+
else
|
327
|
+
# No HD yet - this is expected before OS install
|
328
|
+
# DO NOT clear boot overrides as that would clear one-time boot settings
|
329
|
+
# Just log that HD will be available after OS install
|
330
|
+
debug "No HD found yet (expected before OS install)", 1, :yellow
|
331
|
+
debug "HD will become available after OS installation", 1, :yellow
|
332
|
+
debug "System will boot from HD naturally after the one-time virtual media boot", 1, :yellow
|
333
|
+
result = true # Return success since this is expected
|
334
|
+
end
|
335
|
+
|
336
|
+
result
|
204
337
|
end
|
205
338
|
end
|
206
339
|
end
|