idrac 0.1.23 → 0.1.26
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/README.md +22 -0
- data/lib/idrac/client.rb +93 -40
- data/lib/idrac/firmware.rb +104 -15
- data/lib/idrac/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d751ffb6e590b80a6a5dc72457d8a4be11213224a13ebeae50698ae9f6cafff
|
4
|
+
data.tar.gz: 5a3f826850bf9aee8e40287ff7f8ab7c5bd71944ddce2b183dbe88dabed9c3fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 553bef804c000680be8ecadb15c19f2b4a335db5a7a44b5071ccb230f168880268e906f5a296a70c8659a5bbbb14a0ffb3a6f56cfc4c9ca216e0a1f8a6024a0f
|
7
|
+
data.tar.gz: ff4d0ce0967de6fbddd425844e0c86165276c44d508a2b8de41a20ec54e3998f7d2f1d5aa7cadf34201dd26113bd0abafb1e01d13439edbcb1d22f273e31a1f5
|
data/README.md
CHANGED
@@ -127,6 +127,28 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
127
127
|
|
128
128
|
## Changelog
|
129
129
|
|
130
|
+
### Version 0.1.26
|
131
|
+
- **Improved Redfish Session Creation**: Fixed issues with the Redfish session creation process
|
132
|
+
- Added multiple fallback methods for creating sessions with different iDRAC versions
|
133
|
+
- Fixed 415 Unsupported Media Type error by trying different content types
|
134
|
+
- Added support for form-urlencoded requests when JSON requests fail
|
135
|
+
- Enhanced error handling and logging during session creation
|
136
|
+
|
137
|
+
### Version 0.1.25
|
138
|
+
- **Enhanced Component Matching**: Improved firmware component matching with catalog entries
|
139
|
+
- Added extraction of model numbers and identifiers from component names (X520, H730, etc.)
|
140
|
+
- Implemented multiple matching strategies for better accuracy
|
141
|
+
- Added special handling for different component types (NIC, PERC, BIOS, iDRAC, etc.)
|
142
|
+
- Improved matching for components with different naming conventions
|
143
|
+
|
144
|
+
### Version 0.1.24
|
145
|
+
- **Improved Firmware Update Check**: Fixed issues with firmware version comparison
|
146
|
+
- Eliminated duplicate entries in firmware update results
|
147
|
+
- Improved matching logic between installed firmware and catalog entries
|
148
|
+
- Added proper handling of BIOS updates
|
149
|
+
- Fixed missing version information in update output
|
150
|
+
- Enhanced name comparison with case-insensitive matching
|
151
|
+
|
130
152
|
### Version 0.1.23
|
131
153
|
- **Fixed Duplicate Messages**: Removed duplicate "Retrieving system inventory..." message in firmware:status command
|
132
154
|
- The message was appearing twice because it was being printed by both the CLI class and the Firmware class
|
data/lib/idrac/client.rb
CHANGED
@@ -130,67 +130,120 @@ module IDRAC
|
|
130
130
|
url = '/redfish/v1/SessionService/Sessions'
|
131
131
|
payload = { "UserName" => username, "Password" => password }
|
132
132
|
|
133
|
-
#
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
133
|
+
# Try first with just Content-Type header (no Basic Auth)
|
134
|
+
begin
|
135
|
+
response = connection.post(url) do |req|
|
136
|
+
req.headers['Content-Type'] = 'application/json'
|
137
|
+
req.body = payload.to_json
|
138
|
+
end
|
139
|
+
|
140
|
+
if response.status == 201 || response.status == 200
|
141
|
+
# Extract X-Auth-Token from response headers
|
142
|
+
@x_auth_token = response.headers['X-Auth-Token']
|
143
|
+
|
144
|
+
# Extract session location from response headers
|
145
|
+
@session_location = response.headers['Location']
|
146
|
+
|
147
|
+
puts "Redfish session created successfully"
|
148
|
+
@sessions_maxed = false
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
rescue => e
|
152
|
+
puts "First session creation attempt failed: #{e.message}"
|
142
153
|
end
|
143
154
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
155
|
+
# If that fails, try with Basic Auth
|
156
|
+
begin
|
157
|
+
# Use Basic Auth for the session creation
|
158
|
+
basic_auth_headers = {
|
159
|
+
'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
|
160
|
+
'Content-Type' => 'application/json'
|
161
|
+
}
|
150
162
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
puts "Maximum sessions reached during Redfish session creation"
|
156
|
-
@sessions_maxed = true
|
163
|
+
response = connection.post(url) do |req|
|
164
|
+
req.headers.merge!(basic_auth_headers)
|
165
|
+
req.body = payload.to_json
|
166
|
+
end
|
157
167
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
168
|
+
if response.status == 201 || response.status == 200
|
169
|
+
# Extract X-Auth-Token from response headers
|
170
|
+
@x_auth_token = response.headers['X-Auth-Token']
|
171
|
+
|
172
|
+
# Extract session location from response headers
|
173
|
+
@session_location = response.headers['Location']
|
174
|
+
|
175
|
+
puts "Redfish session created successfully with Basic Auth"
|
176
|
+
@sessions_maxed = false
|
177
|
+
return true
|
178
|
+
elsif response.status == 400 && response.body.include?("maximum number of user sessions")
|
179
|
+
puts "Maximum sessions reached during Redfish session creation"
|
180
|
+
@sessions_maxed = true
|
181
|
+
|
182
|
+
# Try to clear sessions if auto_delete_sessions is enabled
|
183
|
+
if @auto_delete_sessions
|
184
|
+
puts "Auto-delete sessions is enabled, attempting to clear sessions"
|
185
|
+
if force_clear_sessions
|
186
|
+
puts "Successfully cleared sessions, trying to create a new session"
|
187
|
+
|
188
|
+
# Try one more time after clearing
|
189
|
+
response = connection.post(url) do |req|
|
190
|
+
req.headers.merge!(basic_auth_headers)
|
191
|
+
req.body = payload.to_json
|
192
|
+
end
|
193
|
+
|
194
|
+
if response.status == 201 || response.status == 200
|
195
|
+
@x_auth_token = response.headers['X-Auth-Token']
|
196
|
+
@session_location = response.headers['Location']
|
197
|
+
puts "Redfish session created successfully after clearing sessions"
|
198
|
+
@sessions_maxed = false
|
199
|
+
return true
|
200
|
+
else
|
201
|
+
puts "Failed to create Redfish session after clearing: #{response.status} - #{response.body}"
|
202
|
+
# If we still can't create a session, switch to direct mode
|
203
|
+
@direct_mode = true
|
204
|
+
return false
|
205
|
+
end
|
206
|
+
else
|
207
|
+
puts "Failed to clear sessions, switching to direct mode"
|
208
|
+
@direct_mode = true
|
209
|
+
return false
|
210
|
+
end
|
211
|
+
else
|
212
|
+
puts "Auto-delete sessions is disabled, switching to direct mode"
|
213
|
+
@direct_mode = true
|
214
|
+
return false
|
215
|
+
end
|
216
|
+
else
|
217
|
+
puts "Failed to create Redfish session: #{response.status} - #{response.body}"
|
218
|
+
|
219
|
+
# If we get a 415 error, try with form-urlencoded
|
220
|
+
if response.status == 415
|
221
|
+
puts "Trying with form-urlencoded content type"
|
165
222
|
response = connection.post(url) do |req|
|
166
|
-
req.headers
|
167
|
-
req.
|
223
|
+
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
224
|
+
req.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
225
|
+
req.body = "UserName=#{URI.encode_www_form_component(username)}&Password=#{URI.encode_www_form_component(password)}"
|
168
226
|
end
|
169
227
|
|
170
228
|
if response.status == 201 || response.status == 200
|
171
229
|
@x_auth_token = response.headers['X-Auth-Token']
|
172
230
|
@session_location = response.headers['Location']
|
173
|
-
puts "Redfish session created successfully
|
231
|
+
puts "Redfish session created successfully with form-urlencoded"
|
174
232
|
@sessions_maxed = false
|
175
233
|
return true
|
176
234
|
else
|
177
|
-
puts "Failed
|
178
|
-
# If we still can't create a session, switch to direct mode
|
235
|
+
puts "Failed with form-urlencoded too: #{response.status} - #{response.body}"
|
179
236
|
@direct_mode = true
|
180
237
|
return false
|
181
238
|
end
|
182
239
|
else
|
183
|
-
puts "Failed to clear sessions, switching to direct mode"
|
184
240
|
@direct_mode = true
|
185
241
|
return false
|
186
242
|
end
|
187
|
-
else
|
188
|
-
puts "Auto-delete sessions is disabled, switching to direct mode"
|
189
|
-
@direct_mode = true
|
190
|
-
return false
|
191
243
|
end
|
192
|
-
|
193
|
-
puts "
|
244
|
+
rescue => e
|
245
|
+
puts "Error during Redfish session creation: #{e.message}"
|
246
|
+
@direct_mode = true
|
194
247
|
return false
|
195
248
|
end
|
196
249
|
end
|
data/lib/idrac/firmware.rb
CHANGED
@@ -153,7 +153,13 @@ module IDRAC
|
|
153
153
|
# Get current firmware versions
|
154
154
|
current_versions = {}
|
155
155
|
inventory[:firmware].each do |fw|
|
156
|
-
|
156
|
+
# Use the ID as the key to avoid duplicates
|
157
|
+
current_versions[fw[:id]] = {
|
158
|
+
name: fw[:name],
|
159
|
+
version: fw[:version],
|
160
|
+
updateable: fw[:updateable],
|
161
|
+
identifiers: extract_identifiers(fw[:name]) # Extract identifiers for better matching
|
162
|
+
}
|
157
163
|
end
|
158
164
|
|
159
165
|
# Find matching components in catalog
|
@@ -163,29 +169,112 @@ module IDRAC
|
|
163
169
|
path = component.at_xpath('Path')&.text
|
164
170
|
component_type = component.at_xpath('ComponentType')&.text
|
165
171
|
|
172
|
+
next unless name && version && path # Skip if missing essential data
|
173
|
+
|
174
|
+
# Extract identifiers from catalog component name for better matching
|
175
|
+
catalog_identifiers = extract_identifiers(name)
|
176
|
+
|
166
177
|
# Check if this component matches any of our firmware
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
178
|
+
# We'll track if we found a match to avoid duplicates
|
179
|
+
matched = false
|
180
|
+
|
181
|
+
current_versions.each do |id, fw_info|
|
182
|
+
# Skip if not updateable
|
183
|
+
next unless fw_info[:updateable]
|
184
|
+
|
185
|
+
# Normalize names for comparison
|
186
|
+
catalog_name = name.downcase.strip
|
187
|
+
firmware_name = fw_info[:name].downcase.strip
|
188
|
+
|
189
|
+
# Check for matches using multiple strategies
|
190
|
+
match_found = false
|
191
|
+
|
192
|
+
# 1. Check if names contain each other
|
193
|
+
if catalog_name.include?(firmware_name) || firmware_name.include?(catalog_name)
|
194
|
+
match_found = true
|
195
|
+
end
|
196
|
+
|
197
|
+
# 2. Check if BIOS components match
|
198
|
+
if (catalog_name.include?("bios") && firmware_name.include?("bios"))
|
199
|
+
match_found = true
|
200
|
+
end
|
201
|
+
|
202
|
+
# 3. Check if identifiers match
|
203
|
+
if !match_found && !fw_info[:identifiers].empty? && !catalog_identifiers.empty?
|
204
|
+
# Check if any identifier from firmware matches any identifier from catalog
|
205
|
+
if (fw_info[:identifiers] & catalog_identifiers).any?
|
206
|
+
match_found = true
|
181
207
|
end
|
182
208
|
end
|
209
|
+
|
210
|
+
# If we found a match and versions differ, add to updates
|
211
|
+
if match_found && !matched && version != fw_info[:version]
|
212
|
+
updates << {
|
213
|
+
name: name,
|
214
|
+
current_version: fw_info[:version],
|
215
|
+
available_version: version,
|
216
|
+
path: path,
|
217
|
+
component_type: component_type,
|
218
|
+
download_url: "https://downloads.dell.com/#{path}"
|
219
|
+
}
|
220
|
+
|
221
|
+
# Mark as matched to avoid duplicates
|
222
|
+
matched = true
|
223
|
+
end
|
183
224
|
end
|
184
225
|
end
|
185
226
|
|
186
227
|
updates
|
187
228
|
end
|
188
229
|
|
230
|
+
def extract_identifiers(name)
|
231
|
+
return [] unless name
|
232
|
+
|
233
|
+
identifiers = []
|
234
|
+
|
235
|
+
# Extract model numbers like X520, I350, etc.
|
236
|
+
model_matches = name.scan(/[IX]\d{3,4}/)
|
237
|
+
identifiers.concat(model_matches)
|
238
|
+
|
239
|
+
# Extract PERC model like H730
|
240
|
+
perc_matches = name.scan(/[HP]\d{3,4}/)
|
241
|
+
identifiers.concat(perc_matches)
|
242
|
+
|
243
|
+
# Extract other common identifiers
|
244
|
+
if name.include?("NIC") || name.include?("Ethernet") || name.include?("Network")
|
245
|
+
identifiers << "NIC"
|
246
|
+
end
|
247
|
+
|
248
|
+
if name.include?("PERC") || name.include?("RAID")
|
249
|
+
identifiers << "PERC"
|
250
|
+
# Extract PERC model like H730
|
251
|
+
perc_match = name.match(/PERC\s+([A-Z]\d{3})/)
|
252
|
+
identifiers << perc_match[1] if perc_match
|
253
|
+
end
|
254
|
+
|
255
|
+
if name.include?("BIOS")
|
256
|
+
identifiers << "BIOS"
|
257
|
+
end
|
258
|
+
|
259
|
+
if name.include?("iDRAC") || name.include?("IDRAC") || name.include?("Remote Access Controller")
|
260
|
+
identifiers << "iDRAC"
|
261
|
+
end
|
262
|
+
|
263
|
+
if name.include?("Power Supply") || name.include?("PSU")
|
264
|
+
identifiers << "PSU"
|
265
|
+
end
|
266
|
+
|
267
|
+
if name.include?("Lifecycle Controller")
|
268
|
+
identifiers << "LC"
|
269
|
+
end
|
270
|
+
|
271
|
+
if name.include?("CPLD")
|
272
|
+
identifiers << "CPLD"
|
273
|
+
end
|
274
|
+
|
275
|
+
identifiers
|
276
|
+
end
|
277
|
+
|
189
278
|
def interactive_update(catalog_path = nil)
|
190
279
|
updates = check_updates(catalog_path)
|
191
280
|
|
data/lib/idrac/version.rb
CHANGED