Module: Supermicro::Boot

Included in:
Client
Defined in:
lib/supermicro/boot.rb

Instance Method Summary collapse

Instance Method Details

#bootObject

Shorter alias for convenience



76
77
78
# File 'lib/supermicro/boot.rb', line 76

def boot
  boot_config
end

#boot_configObject

Get boot configuration with snake_case fields



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
# File 'lib/supermicro/boot.rb', line 9

def boot_config
  response = authenticated_request(:get, "/redfish/v1/Systems/1")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      boot_data = data["Boot"] || {}
      
      # Get boot options for resolving references
      options_map = {}
      begin
        options = boot_options
        options.each do |opt|
          options_map[opt["id"]] = opt["display_name"] || opt["name"]
        end
      rescue
        # Ignore errors fetching boot options
      end
      
      # Build boot order with resolved names
      boot_order = (boot_data["BootOrder"] || []).map do |ref|
        {
          "reference" => ref,
          "name" => options_map[ref] || ref
        }
      end
      
      # Return hash with snake_case fields
      {
        # Boot override settings (for one-time or continuous boot)
        "boot_source_override_enabled" => boot_data["BootSourceOverrideEnabled"],     # Disabled/Once/Continuous
        "boot_source_override_target" => boot_data["BootSourceOverrideTarget"],       # None/Pxe/Hdd/Cd/etc
        "boot_source_override_mode" => boot_data["BootSourceOverrideMode"],           # UEFI/Legacy
        "allowed_override_targets" => boot_data["[email protected]"] || [],
        
        # Permanent boot order with resolved names
        "boot_order" => boot_order,                                                    # [{reference: "Boot0002", name: "PXE IPv4"}]
        "boot_order_refs" => boot_data["BootOrder"] || [],                            # Raw references for set_boot_order
        
        # Supermicro specific fields
        "boot_next" => boot_data["BootNext"],
        "http_boot_uri" => boot_data["HttpBootUri"],
        
        # References to other resources
        "boot_options_uri" => boot_data.dig("BootOptions", "@odata.id")
      }.compact
    rescue JSON::ParserError
      raise Error, "Failed to parse boot response: #{response.body}"
    end
  else
    raise Error, "Failed to get boot configuration. Status code: #{response.status}"
  end
end

#boot_optionsObject

Get boot options collection (detailed boot devices) with snake_case



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
# File 'lib/supermicro/boot.rb', line 197

def boot_options
  response = authenticated_request(:get, "/redfish/v1/Systems/1/BootOptions")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      options = []
      
      # Supermicro doesn't support $expand, fetch each individually
      data["Members"]&.each do |member|
        if member["@odata.id"]
          opt_response = authenticated_request(:get, member["@odata.id"])
          if opt_response.status == 200
            opt_data = JSON.parse(opt_response.body)
            options << {
              "id" => opt_data["Id"],                                           # Boot0002
              "boot_option_reference" => opt_data["BootOptionReference"],       # Boot0002
              "display_name" => opt_data["DisplayName"],                        # "UEFI PXE IPv4: Intel..."
              "name" => opt_data["DisplayName"] || opt_data["Name"],            # Alias for display_name
              "enabled" => opt_data["BootOptionEnabled"],                       # true/false
              "uefi_device_path" => opt_data["UefiDevicePath"],                 # UEFI device path if present
              "description" => opt_data["Description"]
            }.compact
          end
        end
      end
      
      options
    rescue JSON::ParserError
      raise Error, "Failed to parse boot options response: #{response.body}"
    end
  else
    []
  end
end

#boot_rawObject

Get raw Redfish boot data (CamelCase)



64
65
66
67
68
69
70
71
72
73
# File 'lib/supermicro/boot.rb', line 64

def boot_raw
  response = authenticated_request(:get, "/redfish/v1/Systems/1")
  
  if response.status == 200
    data = JSON.parse(response.body)
    data["Boot"] || {}
  else
    raise Error, "Failed to get boot configuration. Status code: #{response.status}"
  end
end

#boot_to_bios_setup(enabled: "Once", mode: nil) ⇒ Object



256
257
258
# File 'lib/supermicro/boot.rb', line 256

def boot_to_bios_setup(enabled: "Once", mode: nil)
  set_boot_override("BiosSetup", enabled: enabled, mode: mode)
end

#boot_to_cd(enabled: "Once", mode: "UEFI") ⇒ Object



247
248
249
250
# File 'lib/supermicro/boot.rb', line 247

def boot_to_cd(enabled: "Once", mode: "UEFI")
  # Always use UEFI mode for CD boot since we're booting UEFI media
  set_boot_override("Cd", enabled: enabled, mode: mode)
end

#boot_to_disk(enabled: "Once", mode: nil) ⇒ Object



243
244
245
# File 'lib/supermicro/boot.rb', line 243

def boot_to_disk(enabled: "Once", mode: nil)
  set_boot_override("Hdd", enabled: enabled, mode: mode)
end

#boot_to_pxe(enabled: "Once", mode: nil) ⇒ Object

Convenience methods for common boot targets



239
240
241
# File 'lib/supermicro/boot.rb', line 239

def boot_to_pxe(enabled: "Once", mode: nil)
  set_boot_override("Pxe", enabled: enabled, mode: mode)
end

#boot_to_usb(enabled: "Once", mode: nil) ⇒ Object



252
253
254
# File 'lib/supermicro/boot.rb', line 252

def boot_to_usb(enabled: "Once", mode: nil)
  set_boot_override("Usb", enabled: enabled, mode: mode)
end

#clear_boot_overrideObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/supermicro/boot.rb', line 148

def clear_boot_override
  puts "Clearing boot override...".yellow
  
  body = {
    "Boot" => {
      "BootSourceOverrideEnabled" => "Disabled"
    }
  }
  
  response = authenticated_request(
    :patch,
    "/redfish/v1/Systems/1",
    body: body.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  if response.status.between?(200, 299)
    puts "Boot override cleared successfully.".green
    return true
  else
    raise Error, "Failed to clear boot override: #{response.status} - #{response.body}"
  end
end

#configure_boot_settings(persistence: nil, mode: nil) ⇒ Object



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
# File 'lib/supermicro/boot.rb', line 118

def configure_boot_settings(persistence: nil, mode: nil)
  debug "Configuring boot settings..."
  
  body = { "Boot" => {} }
  
  if persistence
    body["Boot"]["BootSourceOverrideEnabled"] = persistence
  end
  
  if mode
    body["Boot"]["BootSourceOverrideMode"] = mode
  end
  
  return false if body["Boot"].empty?
  
  response = authenticated_request(
    :patch,
    "/redfish/v1/Systems/1",
    body: body.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  if response.status.between?(200, 299)
    debug "Boot settings configured successfully."
    return true
  else
    raise Error, "Failed to configure boot settings: #{response.status} - #{response.body}"
  end
end

#get_boot_devicesObject

Alias for backwards compatibility



234
235
236
# File 'lib/supermicro/boot.rb', line 234

def get_boot_devices
  boot_options
end

#set_boot_order(devices) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/supermicro/boot.rb', line 172

def set_boot_order(devices)
  puts "Setting boot order...".yellow
  
  body = {
    "Boot" => {
      "BootOrder" => devices
    }
  }
  
  response = authenticated_request(
    :patch,
    "/redfish/v1/Systems/1",
    body: body.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  if response.status.between?(200, 299)
    puts "Boot order set successfully.".green
    return true
  else
    raise Error, "Failed to set boot order: #{response.status} - #{response.body}"
  end
end

#set_boot_order_hd_firstObject

Set boot order with hard drive first



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/supermicro/boot.rb', line 304

def set_boot_order_hd_first
  debug "Configuring system to boot from HD after OS installation...", 1, :yellow
  
  # First check if there's actually a hard disk present
  boot_opts = boot_options
  hd_option = boot_opts.find { |opt| 
    opt["display_name"] =~ /Hard Drive|HDD|SATA|NVMe|SSD|RAID|UEFI OS/i 
  }
  
  if hd_option
    # HD exists, set it as first in boot order
    other_options = boot_opts.select { |opt| 
      opt["enabled"] && opt["id"] != hd_option["id"]
    }.sort_by { |opt| opt["id"] }
    
    new_order = [hd_option["id"]] + other_options.map { |opt| opt["id"] }
    debug "Setting boot order with HD first: #{new_order.join(', ')}", 2, :yellow
    result = set_boot_order(new_order)
    
    if result
      debug "Boot order set with HD first", 1, :green
    end
  else
    # No HD yet - this is expected before OS install
    # DO NOT clear boot overrides as that would clear one-time boot settings
    # Just log that HD will be available after OS install
    debug "No HD found yet (expected before OS install)", 1, :yellow
    debug "HD will become available after OS installation", 1, :yellow
    debug "System will boot from HD naturally after the one-time virtual media boot", 1, :yellow
    result = true  # Return success since this is expected
  end
  
  result
end

#set_boot_override(target, enabled: "Once", mode: nil) ⇒ Object

Set boot override for next boot



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
# File 'lib/supermicro/boot.rb', line 81

def set_boot_override(target, enabled: "Once", mode: nil)
  # Validate target against allowed values
  boot_data = boot
  valid_targets = boot_data["allowed_override_targets"]
  
  if valid_targets && !valid_targets.include?(target)
    debug "Invalid boot target '#{target}'. Allowed values: #{valid_targets.join(', ')}"
    raise Error, "Invalid boot target: #{target}"
  end
  
  debug "Setting boot override to #{target} (#{enabled})..."
  
  body = {
    "Boot" => {
      "BootSourceOverrideEnabled" => enabled,  # Disabled/Once/Continuous
      "BootSourceOverrideTarget" => target     # None/Pxe/Hdd/Cd/etc
    }
  }
  
  # Add boot mode if specified
  body["Boot"]["BootSourceOverrideMode"] = mode if mode
  
  response = authenticated_request(
    :patch,
    "/redfish/v1/Systems/1",
    body: body.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  if response.status.between?(200, 299)
    debug "Boot override set successfully."
    return true
  else
    raise Error, "Failed to set boot override: #{response.status} - #{response.body}"
  end
end

#set_one_time_boot_to_virtual_mediaObject

Set one-time boot to virtual media (CD)



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/supermicro/boot.rb', line 261

def set_one_time_boot_to_virtual_media
  debug "Setting one-time boot to virtual media...", 1, :yellow
  
  # Supermicro often needs virtual media remounted to ensure it's properly recognized
  # Check if virtual media is already mounted and remount if so
  begin
    require_relative 'virtual_media'
    vm_status = virtual_media_status
    
    if vm_status && vm_status["Inserted"]
      current_image = vm_status["Image"]
      if current_image
        debug "Remounting virtual media to ensure fresh connection...", 1, :yellow
        
        # Eject current media
        eject_virtual_media rescue nil
        sleep 2
        
        # Re-insert the media
        insert_virtual_media(current_image)
        sleep 3
        
        debug "Virtual media remounted: #{current_image}", 1, :green
      end
    end
  rescue => e
    debug "Note: Could not remount virtual media: #{e.message}", 2, :yellow
  end
  
  # Now try the standard boot override - this often works after remount
  result = boot_to_cd(enabled: "Once")
  
  if result
    debug "One-time boot to virtual media configured", 1, :green
    debug "System will boot from virtual CD on next restart", 1, :green
  else
    debug "Failed to set boot override, may need manual intervention", 1, :red
  end
  
  result
end