idrac 0.7.10 → 0.8.1
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 +28 -0
- data/bin/idrac +47 -0
- data/idrac.gemspec +4 -4
- data/lib/idrac/client.rb +6 -0
- data/lib/idrac/firmware.rb +3 -3
- data/lib/idrac/storage.rb +1 -1
- data/lib/idrac/system.rb +5 -5
- data/lib/idrac/utility.rb +350 -2
- data/lib/idrac/version.rb +1 -1
- metadata +9 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 527454266f07cf2a797f42c324d9a8a17ddc2586f72aa00af935500ea4386ef1
|
4
|
+
data.tar.gz: 3ff97e72d6947dd6c08e34d7682daa579b3bf3d4f505726feb4ab280172db56e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a76db748a58725fe2c6409c40df95449d5a648a9647a816e8653f2c31adcb519d2d88a2e1a452e902ce7efb65d868f78e2a9bc09bf4952aaf141ed1b518b9836
|
7
|
+
data.tar.gz: 1e2bca28672a8ac30ad0573040374d016417a77cc6c264fb5434ce1ed40561260cccb54eb58385aeebe749fc27de20d780adeee82a67dabc7a49f54e299033bb
|
data/README.md
CHANGED
@@ -17,6 +17,8 @@ A Ruby client for the Dell iDRAC API. This gem provides a command-line interface
|
|
17
17
|
- Lifecycle Controller status management
|
18
18
|
- Return values as RecursiveOpenStruct objects for convenient attribute access
|
19
19
|
- Reset iDRAC functionality
|
20
|
+
- Generate and download TSR (Technical Support Report) / SupportAssist collections
|
21
|
+
- SupportAssist EULA management
|
20
22
|
|
21
23
|
## Installation
|
22
24
|
|
@@ -85,6 +87,11 @@ idrac sel:clear --host=192.168.1.100
|
|
85
87
|
|
86
88
|
# Reset iDRAC
|
87
89
|
idrac reset --host=192.168.1.100
|
90
|
+
|
91
|
+
# TSR/SupportAssist Collection Commands
|
92
|
+
idrac tsr_collect --host=192.168.1.100 # Generate and download TSR logs
|
93
|
+
idrac tsr_status --host=192.168.1.100 # Check TSR collection status
|
94
|
+
idrac tsr_accept_eula --host=192.168.1.100 # Accept SupportAssist EULA (required before first use)
|
88
95
|
```
|
89
96
|
|
90
97
|
All commands automatically handle session expiration by re-authenticating when necessary, ensuring that long-running operations like firmware updates complete successfully even if the iDRAC session times out.
|
@@ -179,6 +186,18 @@ client.clear_lifecycle!
|
|
179
186
|
# Clear System Event Logs
|
180
187
|
client.clear_system_event_logs!
|
181
188
|
|
189
|
+
# TSR/SupportAssist Collection operations
|
190
|
+
# Accept EULA (required only once)
|
191
|
+
client.accept_supportassist_eula
|
192
|
+
|
193
|
+
# Generate and download TSR logs
|
194
|
+
file_path = client.generate_and_download_tsr(output_file: "tsr.zip")
|
195
|
+
puts "TSR logs saved to: #{file_path}"
|
196
|
+
|
197
|
+
# Check TSR collection status
|
198
|
+
status = client.tsr_status
|
199
|
+
puts "Collection available: #{status[:available]}"
|
200
|
+
|
182
201
|
# Working with hash objects
|
183
202
|
# Methods return data as Ruby hashes with string keys for consistent access
|
184
203
|
|
@@ -272,6 +291,15 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
272
291
|
|
273
292
|
## Changelog
|
274
293
|
|
294
|
+
### Version 0.8.0
|
295
|
+
- **Added TSR/SupportAssist Collection Support**: Simplified commands for generating Technical Support Reports
|
296
|
+
- Generate and download SupportAssist collections with direct file download
|
297
|
+
- Automatic EULA acceptance check with clear user guidance
|
298
|
+
- Simple CLI commands: `tsr_collect`, `tsr_status`, `tsr_accept_eula`
|
299
|
+
- Job-based generation with automatic progress tracking
|
300
|
+
- Direct download approach based on Dell's official Python implementation
|
301
|
+
- Clear error messages when EULA acceptance is required
|
302
|
+
|
275
303
|
### Version 0.7.8
|
276
304
|
- **Network Redirection Support**: Added optional `host_header` parameter to Client initialization
|
277
305
|
- Enables iDRAC access through network redirection scenarios where the connection IP differs from the Host header requirement
|
data/bin/idrac
CHANGED
@@ -1660,6 +1660,53 @@ module IDRAC
|
|
1660
1660
|
end
|
1661
1661
|
end
|
1662
1662
|
|
1663
|
+
# TSR (Technical Support Report) commands
|
1664
|
+
desc "tsr_collect", "Generate and download TSR/SupportAssist collection"
|
1665
|
+
method_option :output, type: :string, desc: "Output filename"
|
1666
|
+
def tsr_collect
|
1667
|
+
with_idrac_client do |client|
|
1668
|
+
puts "Generating and downloading TSR logs...".yellow
|
1669
|
+
puts "This may take several minutes...".yellow
|
1670
|
+
|
1671
|
+
result = client.generate_and_download_tsr(output_file: options[:output])
|
1672
|
+
|
1673
|
+
if result
|
1674
|
+
puts "TSR logs saved to: #{result}".green
|
1675
|
+
puts "File size: #{File.size(result) / 1024 / 1024} MB".cyan if File.exist?(result)
|
1676
|
+
else
|
1677
|
+
puts "Failed to generate or download TSR logs".red
|
1678
|
+
end
|
1679
|
+
end
|
1680
|
+
end
|
1681
|
+
|
1682
|
+
desc "tsr_status", "Check TSR collection status"
|
1683
|
+
def tsr_status
|
1684
|
+
with_idrac_client do |client|
|
1685
|
+
status = client.tsr_status
|
1686
|
+
|
1687
|
+
puts "\nTSR Collection Status:".green.bold
|
1688
|
+
puts "Available: #{status[:available] ? 'Yes'.green : 'No'.red}"
|
1689
|
+
|
1690
|
+
if status[:collection_in_progress]
|
1691
|
+
puts "Collection in progress: Yes".yellow
|
1692
|
+
puts "Job ID: #{status[:job_id]}".cyan if status[:job_id]
|
1693
|
+
else
|
1694
|
+
puts "Collection in progress: No".green
|
1695
|
+
end
|
1696
|
+
end
|
1697
|
+
end
|
1698
|
+
|
1699
|
+
desc "tsr_accept_eula", "Accept SupportAssist EULA"
|
1700
|
+
def tsr_accept_eula
|
1701
|
+
with_idrac_client do |client|
|
1702
|
+
if client.accept_supportassist_eula
|
1703
|
+
puts "SupportAssist EULA accepted successfully".green
|
1704
|
+
else
|
1705
|
+
puts "Failed to accept SupportAssist EULA".red
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
end
|
1709
|
+
|
1663
1710
|
private
|
1664
1711
|
|
1665
1712
|
def with_idrac_client
|
data/idrac.gemspec
CHANGED
@@ -10,14 +10,14 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = "API Client for Dell iDRAC"
|
12
12
|
spec.description = "A Ruby client for the Dell iDRAC API"
|
13
|
-
spec.homepage = "
|
13
|
+
spec.homepage = "https://github.com/buildio/idrac"
|
14
14
|
spec.license = "MIT"
|
15
|
-
spec.required_ruby_version = ">=
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
16
16
|
|
17
17
|
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
18
|
|
19
19
|
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
-
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/buildio/idrac"
|
21
21
|
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
22
22
|
|
23
23
|
# Specify which files should be added to the gem when it is released.
|
@@ -41,7 +41,7 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_dependency "thor", "~> 1.2", ">= 1.2.0"
|
42
42
|
spec.add_dependency "base64", "~> 0.1", ">= 0.1.0"
|
43
43
|
spec.add_dependency "colorize", "~> 1.1", ">= 1.1.0"
|
44
|
-
spec.add_dependency "activesupport", ">=
|
44
|
+
spec.add_dependency "activesupport", ">= 7.0"
|
45
45
|
|
46
46
|
# Development dependencies
|
47
47
|
spec.add_development_dependency "bundler", "~> 2.4", ">= 2.4.0"
|
data/lib/idrac/client.rb
CHANGED
@@ -155,6 +155,11 @@ module IDRAC
|
|
155
155
|
headers['Accept'] ||= 'application/json'
|
156
156
|
headers['Host'] = @host_header if @host_header
|
157
157
|
|
158
|
+
# Debug the body being sent
|
159
|
+
if body && @verbosity >= 2
|
160
|
+
debug "Request body: #{body}", 2
|
161
|
+
end
|
162
|
+
|
158
163
|
# Determine authentication method and set headers
|
159
164
|
if @direct_mode
|
160
165
|
headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
@@ -190,6 +195,7 @@ module IDRAC
|
|
190
195
|
begin
|
191
196
|
conn.options.timeout = timeout if timeout
|
192
197
|
conn.options.open_timeout = open_timeout if open_timeout
|
198
|
+
|
193
199
|
conn.run_request(method, path, body, headers)
|
194
200
|
ensure
|
195
201
|
conn.options.timeout = original_timeout
|
data/lib/idrac/firmware.rb
CHANGED
@@ -57,7 +57,7 @@ module IDRAC
|
|
57
57
|
puts "Retrieving system inventory..."
|
58
58
|
|
59
59
|
# Get basic system information
|
60
|
-
system_uri = URI.parse("#{client.base_url}/redfish/v1/Systems/System.Embedded.1")
|
60
|
+
# system_uri = URI.parse("#{client.base_url}/redfish/v1/Systems/System.Embedded.1") # Not used, kept for reference
|
61
61
|
system_response = client.authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
|
62
62
|
|
63
63
|
if system_response.status != 200
|
@@ -67,7 +67,7 @@ module IDRAC
|
|
67
67
|
system_data = JSON.parse(system_response.body)
|
68
68
|
|
69
69
|
# Get firmware inventory
|
70
|
-
firmware_uri = URI.parse("#{client.base_url}/redfish/v1/UpdateService/FirmwareInventory")
|
70
|
+
# firmware_uri = URI.parse("#{client.base_url}/redfish/v1/UpdateService/FirmwareInventory") # Not used, kept for reference
|
71
71
|
firmware_response = client.authenticated_request(:get, "/redfish/v1/UpdateService/FirmwareInventory")
|
72
72
|
|
73
73
|
if firmware_response.status != 200
|
@@ -479,7 +479,7 @@ module IDRAC
|
|
479
479
|
end
|
480
480
|
|
481
481
|
# Upload the firmware file
|
482
|
-
file_content = File.read(firmware_path)
|
482
|
+
# file_content = File.read(firmware_path) # Not used directly, file is read by Faraday
|
483
483
|
|
484
484
|
headers = {
|
485
485
|
'Content-Type' => 'multipart/form-data',
|
data/lib/idrac/storage.rb
CHANGED
@@ -212,7 +212,7 @@ module IDRAC
|
|
212
212
|
if controller_comp
|
213
213
|
# Try to find the matching virtual disk
|
214
214
|
# Format is typically "Disk.Virtual.X:RAID...."
|
215
|
-
vd_name = vol["Id"] || vol["Name"]
|
215
|
+
# vd_name = vol["Id"] || vol["Name"] # Not used, kept for debugging
|
216
216
|
vd_comp = controller_comp["Components"]&.find do |comp|
|
217
217
|
comp["FQDD"] =~ /Disk\.Virtual\.\d+:#{controller_fqdd}/
|
218
218
|
end
|
data/lib/idrac/system.rb
CHANGED
@@ -142,7 +142,7 @@ module IDRAC
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
end
|
145
|
-
rescue
|
145
|
+
rescue
|
146
146
|
# Ignore errors, just continue
|
147
147
|
end
|
148
148
|
|
@@ -152,7 +152,7 @@ module IDRAC
|
|
152
152
|
if idrac_net && idrac_net["mac"] && idrac_net["ipv4"]
|
153
153
|
nic_ip_map[idrac_net["mac"].upcase] = idrac_net["ipv4"]
|
154
154
|
end
|
155
|
-
rescue
|
155
|
+
rescue
|
156
156
|
# Ignore errors, just continue
|
157
157
|
end
|
158
158
|
|
@@ -218,7 +218,7 @@ module IDRAC
|
|
218
218
|
break
|
219
219
|
end
|
220
220
|
end
|
221
|
-
rescue
|
221
|
+
rescue
|
222
222
|
# Ignore errors and try the next port type
|
223
223
|
end
|
224
224
|
end
|
@@ -775,7 +775,7 @@ module IDRAC
|
|
775
775
|
|
776
776
|
# Try to get license information (new approach that works with iDRAC 8 too)
|
777
777
|
license_info = license_info() rescue nil
|
778
|
-
license_version = license_version() rescue nil
|
778
|
+
# license_version = license_version() rescue nil # Not used currently
|
779
779
|
|
780
780
|
if license_info
|
781
781
|
license_type = license_info["LicenseType"] || "Unknown"
|
@@ -819,4 +819,4 @@ module IDRAC
|
|
819
819
|
end
|
820
820
|
end
|
821
821
|
end
|
822
|
-
end
|
822
|
+
end
|
data/lib/idrac/utility.rb
CHANGED
@@ -1,7 +1,355 @@
|
|
1
|
+
|
1
2
|
module IDRAC
|
2
3
|
module Utility
|
3
4
|
include Debuggable
|
4
5
|
|
6
|
+
# Generate TSR (Technical Support Report) logs using SupportAssistCollection for local generation
|
7
|
+
# @param data_selector_values [Array] Array of log types to include (optional)
|
8
|
+
# Default includes all available log types
|
9
|
+
# @return [Hash] Result hash with status and job/task information
|
10
|
+
def generate_tsr_logs(data_selector_values: nil, share_type: nil, share_parameters: nil)
|
11
|
+
debug "Generating TSR/SupportAssist logs...", 1
|
12
|
+
|
13
|
+
# Check EULA status first
|
14
|
+
eula_status = supportassist_eula_status
|
15
|
+
if eula_status["EULAAccepted"] == false || eula_status["EULAAccepted"] == "false"
|
16
|
+
puts "\n" + "="*80
|
17
|
+
puts "ERROR: SupportAssist EULA Not Accepted".red.bold
|
18
|
+
puts "="*80
|
19
|
+
puts ""
|
20
|
+
puts "The SupportAssist End User License Agreement (EULA) must be accepted".yellow
|
21
|
+
puts "before you can generate TSR/SupportAssist collections.".yellow
|
22
|
+
puts ""
|
23
|
+
puts "To accept the EULA, run:".cyan
|
24
|
+
puts " idrac tsr_accept_eula --host #{@host} --port #{@port}".green.bold
|
25
|
+
puts ""
|
26
|
+
puts "="*80
|
27
|
+
return { status: :failed, error: "SupportAssist EULA not accepted" }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Default data selector values for comprehensive TSR
|
31
|
+
# Valid values for SupportAssistCollection: "DebugLogs", "GPULogs", "HWData", "OSAppData", "TTYLogs", "TelemetryReports"
|
32
|
+
data_selector_values ||= ["HWData", "OSAppData"]
|
33
|
+
|
34
|
+
# Map numeric values to iDRAC expected strings if needed
|
35
|
+
if data_selector_values.is_a?(Array) && data_selector_values.first.to_s =~ /^\d+$/
|
36
|
+
data_selector_values = data_selector_values.map do |val|
|
37
|
+
case val.to_s
|
38
|
+
when "0" then "HWData"
|
39
|
+
when "1" then "OSAppData"
|
40
|
+
when "2" then "TTYLogs"
|
41
|
+
when "3" then "DebugLogs"
|
42
|
+
else "HWData" # Default to HWData
|
43
|
+
end
|
44
|
+
end
|
45
|
+
elsif data_selector_values.is_a?(String)
|
46
|
+
data_selector_values = data_selector_values.split(',')
|
47
|
+
end
|
48
|
+
|
49
|
+
debug "Data selector values: #{data_selector_values.inspect}", 1
|
50
|
+
|
51
|
+
# Use SupportAssistCollection for local generation as it supports "Local" ShareType
|
52
|
+
payload = {
|
53
|
+
"ShareType" => "Local",
|
54
|
+
"DataSelectorArrayIn" => data_selector_values,
|
55
|
+
"Filter" => "No", # Don't filter PII
|
56
|
+
"Transmit" => "No" # Don't transmit to Dell
|
57
|
+
}
|
58
|
+
|
59
|
+
debug "SupportAssist collection payload: #{payload.to_json}", 1
|
60
|
+
|
61
|
+
response = authenticated_request(
|
62
|
+
:post,
|
63
|
+
"/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistCollection",
|
64
|
+
body: payload.to_json,
|
65
|
+
headers: { 'Content-Type' => 'application/json' }
|
66
|
+
)
|
67
|
+
|
68
|
+
case response.status
|
69
|
+
when 202
|
70
|
+
# Accepted - job created
|
71
|
+
location = response.headers["location"]
|
72
|
+
if location
|
73
|
+
debug "TSR generation job created: #{location}", 1, :green
|
74
|
+
job_id = location.split("/").last
|
75
|
+
# Wait for job to complete and capture the file location
|
76
|
+
job_result = wait_for_job_with_location(job_id)
|
77
|
+
if job_result && (job_result["JobState"] == "Completed" || job_result["JobState"] == "CompletedWithErrors")
|
78
|
+
result = { status: :success, job: job_result }
|
79
|
+
# Check if we got a file location from the job completion
|
80
|
+
result[:location] = job_result["FileLocation"] if job_result["FileLocation"]
|
81
|
+
result
|
82
|
+
else
|
83
|
+
{ status: :failed, error: "Job did not complete successfully" }
|
84
|
+
end
|
85
|
+
else
|
86
|
+
{ status: :accepted, message: "TSR generation initiated" }
|
87
|
+
end
|
88
|
+
when 200..299
|
89
|
+
debug "TSR generation completed immediately", 1, :green
|
90
|
+
{ status: :success }
|
91
|
+
else
|
92
|
+
error_msg = parse_error_response(response)
|
93
|
+
debug "Failed to generate TSR: #{error_msg}", 1, :red
|
94
|
+
{ status: :failed, error: error_msg }
|
95
|
+
end
|
96
|
+
rescue => e
|
97
|
+
debug "Error generating TSR: #{e.message}", 1, :red
|
98
|
+
{ status: :error, error: e.message }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Download TSR/SupportAssist logs from a URL location
|
102
|
+
# @param location [String] URL location of the TSR file
|
103
|
+
# @param output_file [String] Path to save the TSR file (optional)
|
104
|
+
# @return [String, nil] Path to downloaded file or nil if failed
|
105
|
+
def download_tsr_from_location(location, output_file: nil)
|
106
|
+
debug "Downloading TSR from location: #{location}", 1
|
107
|
+
|
108
|
+
# Default output filename with timestamp
|
109
|
+
output_file ||= "supportassist_#{@host}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip"
|
110
|
+
|
111
|
+
# Download the file from the location
|
112
|
+
file_response = authenticated_request(:get, location)
|
113
|
+
|
114
|
+
if file_response.status == 200 && file_response.body
|
115
|
+
File.open(output_file, 'wb') do |f|
|
116
|
+
f.write(file_response.body)
|
117
|
+
end
|
118
|
+
debug "TSR saved to: #{output_file} (#{File.size(output_file)} bytes)", 1, :green
|
119
|
+
return output_file
|
120
|
+
else
|
121
|
+
debug "Failed to download file from location. Status: #{file_response.status}", 1, :red
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
rescue => e
|
125
|
+
debug "Error downloading TSR: #{e.message}", 1, :red
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
# Wait for job and capture file location from response headers
|
130
|
+
# @param job_id [String] The job ID to wait for
|
131
|
+
# @param max_wait [Integer] Maximum time to wait in seconds
|
132
|
+
# @return [Hash, nil] Job data with FileLocation if available
|
133
|
+
def wait_for_job_with_location(job_id, max_wait: 600)
|
134
|
+
debug "Waiting for job #{job_id} to complete...", 1
|
135
|
+
start_time = Time.now
|
136
|
+
|
137
|
+
while (Time.now - start_time) < max_wait
|
138
|
+
job_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/#{job_id}")
|
139
|
+
|
140
|
+
if job_response.status == 200
|
141
|
+
job_data = JSON.parse(job_response.body)
|
142
|
+
|
143
|
+
case job_data["JobState"]
|
144
|
+
when "Completed", "CompletedWithErrors"
|
145
|
+
debug "Job #{job_id} completed: #{job_data["JobState"]}", 1, :green
|
146
|
+
|
147
|
+
# Check response headers for file location
|
148
|
+
if job_response.headers["location"]
|
149
|
+
job_data["FileLocation"] = job_response.headers["location"]
|
150
|
+
debug "Found file location in headers: #{job_data["FileLocation"]}", 1, :green
|
151
|
+
end
|
152
|
+
|
153
|
+
# Also check the job data itself for output location
|
154
|
+
if job_data["Oem"] && job_data["Oem"]["Dell"] && job_data["Oem"]["Dell"]["OutputLocation"]
|
155
|
+
job_data["FileLocation"] = job_data["Oem"]["Dell"]["OutputLocation"]
|
156
|
+
debug "Found file location in job data: #{job_data["FileLocation"]}", 1, :green
|
157
|
+
end
|
158
|
+
|
159
|
+
return job_data
|
160
|
+
when "Failed", "Exception"
|
161
|
+
debug "Job #{job_id} failed: #{job_data["Message"]}", 1, :red
|
162
|
+
return job_data
|
163
|
+
else
|
164
|
+
debug "Job #{job_id} state: #{job_data["JobState"]} - #{job_data["PercentComplete"]}%", 2
|
165
|
+
sleep 5
|
166
|
+
end
|
167
|
+
else
|
168
|
+
debug "Failed to get job status: #{job_response.status}", 2
|
169
|
+
sleep 5
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
debug "Timeout waiting for job #{job_id}", 1, :red
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
|
177
|
+
# Parse error response from iDRAC
|
178
|
+
def parse_error_response(response)
|
179
|
+
begin
|
180
|
+
data = JSON.parse(response.body)
|
181
|
+
if data["error"] && data["error"]["@Message.ExtendedInfo"]
|
182
|
+
data["error"]["@Message.ExtendedInfo"].first["Message"]
|
183
|
+
elsif data["error"] && data["error"]["message"]
|
184
|
+
data["error"]["message"]
|
185
|
+
else
|
186
|
+
"Status: #{response.status} - #{response.body}"
|
187
|
+
end
|
188
|
+
rescue
|
189
|
+
"Status: #{response.status} - #{response.body}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Generate and download TSR logs in a single operation
|
194
|
+
# @param output_file [String] Path to save the TSR file (optional)
|
195
|
+
# @param data_selector_values [Array] Array of log types to include (optional)
|
196
|
+
# @param wait_timeout [Integer] Maximum time to wait for generation in seconds (default: 600)
|
197
|
+
# @return [String, nil] Path to downloaded file or nil if failed
|
198
|
+
def generate_and_download_tsr(output_file: nil, data_selector_values: nil, wait_timeout: 600)
|
199
|
+
debug "Starting TSR generation and download process...", 1
|
200
|
+
|
201
|
+
output_file ||= "supportassist_#{@host}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip"
|
202
|
+
|
203
|
+
# First, generate the TSR
|
204
|
+
result = generate_tsr_logs(data_selector_values: data_selector_values)
|
205
|
+
|
206
|
+
if result[:status] == :success && result[:job]
|
207
|
+
debug "TSR generation completed successfully", 1, :green
|
208
|
+
|
209
|
+
# Check if the job response has a location for the file
|
210
|
+
if result[:location]
|
211
|
+
return download_tsr_from_location(result[:location], output_file: output_file)
|
212
|
+
else
|
213
|
+
# Try alternative download methods based on Dell's Python script approach
|
214
|
+
debug "Attempting to locate generated TSR file...", 1, :yellow
|
215
|
+
|
216
|
+
# Wait a moment for the file to be available
|
217
|
+
sleep 2
|
218
|
+
|
219
|
+
# Try known endpoints where the file might be available
|
220
|
+
possible_locations = [
|
221
|
+
"/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/ExportedFiles/SupportAssist",
|
222
|
+
"/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/ExportedFiles",
|
223
|
+
"/downloads/supportassist_collection.zip",
|
224
|
+
"/sysmgmt/2016/server/support_assist_collection"
|
225
|
+
]
|
226
|
+
|
227
|
+
possible_locations.each do |location|
|
228
|
+
debug "Trying location: #{location}", 2
|
229
|
+
file_response = authenticated_request(:get, location)
|
230
|
+
|
231
|
+
if file_response.status == 200 && file_response.body && file_response.body.size > 1024
|
232
|
+
File.open(output_file, 'wb') do |f|
|
233
|
+
f.write(file_response.body)
|
234
|
+
end
|
235
|
+
debug "TSR saved to: #{output_file} (#{File.size(output_file)} bytes)", 1, :green
|
236
|
+
return output_file
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
debug "Could not locate TSR file for direct download", 1, :yellow
|
241
|
+
debug "The collection was generated but may require network share export", 1, :yellow
|
242
|
+
end
|
243
|
+
elsif result[:status] == :accepted
|
244
|
+
debug "TSR generation was accepted but status unknown", 1, :yellow
|
245
|
+
else
|
246
|
+
debug "Failed to initiate TSR generation: #{result[:error]}", 1, :red
|
247
|
+
end
|
248
|
+
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
public
|
252
|
+
|
253
|
+
# Get TSR/SupportAssist collection status
|
254
|
+
# @return [Hash] Status information
|
255
|
+
def tsr_status
|
256
|
+
debug "Checking SupportAssist collection status...", 1
|
257
|
+
|
258
|
+
response = authenticated_request(
|
259
|
+
:get,
|
260
|
+
"/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService"
|
261
|
+
)
|
262
|
+
|
263
|
+
if response.status == 200
|
264
|
+
data = JSON.parse(response.body)
|
265
|
+
status = {
|
266
|
+
available: data["Actions"]&.key?("#DellLCService.SupportAssistCollection"),
|
267
|
+
export_available: data["Actions"]&.key?("#DellLCService.SupportAssistExportLastCollection"),
|
268
|
+
collection_in_progress: false
|
269
|
+
}
|
270
|
+
|
271
|
+
# Check if there's an active collection job
|
272
|
+
jobs_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs")
|
273
|
+
if jobs_response.status == 200
|
274
|
+
jobs_data = JSON.parse(jobs_response.body)
|
275
|
+
if jobs_data["Members"]
|
276
|
+
jobs_data["Members"].each do |job|
|
277
|
+
if job["Name"]&.include?("SupportAssist") || job["Name"]&.include?("TSR")
|
278
|
+
status[:collection_in_progress] = true
|
279
|
+
status[:job_id] = job["Id"]
|
280
|
+
status[:job_state] = job["JobState"]
|
281
|
+
break
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
debug "SupportAssist status: #{status.to_json}", 2
|
288
|
+
status
|
289
|
+
else
|
290
|
+
debug "Failed to get SupportAssist status: #{response.status}", 1, :red
|
291
|
+
{ available: false, error: "Unable to determine status" }
|
292
|
+
end
|
293
|
+
rescue => e
|
294
|
+
debug "Error checking SupportAssist status: #{e.message}", 1, :red
|
295
|
+
{ available: false, error: e.message }
|
296
|
+
end
|
297
|
+
|
298
|
+
# Check SupportAssist EULA status
|
299
|
+
# @return [Hash] EULA status information
|
300
|
+
def supportassist_eula_status
|
301
|
+
debug "Checking SupportAssist EULA status...", 1
|
302
|
+
|
303
|
+
response = authenticated_request(
|
304
|
+
:post,
|
305
|
+
"/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistGetEULAStatus",
|
306
|
+
body: {}.to_json,
|
307
|
+
headers: { 'Content-Type' => 'application/json' }
|
308
|
+
)
|
309
|
+
|
310
|
+
if response.status.between?(200, 299)
|
311
|
+
begin
|
312
|
+
data = JSON.parse(response.body)
|
313
|
+
debug "EULA status: #{data.to_json}", 2
|
314
|
+
return data
|
315
|
+
rescue JSON::ParserError
|
316
|
+
return { "EULAAccepted" => "Unknown" }
|
317
|
+
end
|
318
|
+
else
|
319
|
+
error_msg = parse_error_response(response)
|
320
|
+
debug "Failed to get EULA status: #{error_msg}", 1, :red
|
321
|
+
return { "EULAAccepted" => "Error", "error" => error_msg }
|
322
|
+
end
|
323
|
+
rescue => e
|
324
|
+
debug "Error checking EULA status: #{e.message}", 1, :red
|
325
|
+
{ "EULAAccepted" => "Error", "error" => e.message }
|
326
|
+
end
|
327
|
+
|
328
|
+
# Accept SupportAssist EULA
|
329
|
+
# @return [Boolean] true if successful
|
330
|
+
def accept_supportassist_eula
|
331
|
+
debug "Accepting SupportAssist EULA...", 1
|
332
|
+
|
333
|
+
response = authenticated_request(
|
334
|
+
:post,
|
335
|
+
"/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistAcceptEULA",
|
336
|
+
body: {}.to_json,
|
337
|
+
headers: { 'Content-Type' => 'application/json' }
|
338
|
+
)
|
339
|
+
|
340
|
+
if response.status.between?(200, 299)
|
341
|
+
debug "SupportAssist EULA accepted successfully", 1, :green
|
342
|
+
true
|
343
|
+
else
|
344
|
+
error_msg = parse_error_response(response)
|
345
|
+
debug "Failed to accept EULA: #{error_msg}", 1, :red
|
346
|
+
false
|
347
|
+
end
|
348
|
+
rescue => e
|
349
|
+
debug "Error accepting EULA: #{e.message}", 1, :red
|
350
|
+
false
|
351
|
+
end
|
352
|
+
|
5
353
|
# Reset the iDRAC controller (graceful restart)
|
6
354
|
def reset!
|
7
355
|
debug "Resetting iDRAC controller...", 1
|
@@ -26,7 +374,7 @@ module IDRAC
|
|
26
374
|
break
|
27
375
|
end
|
28
376
|
sleep 30
|
29
|
-
rescue
|
377
|
+
rescue
|
30
378
|
tries += 1
|
31
379
|
if tries > 5
|
32
380
|
debug "Failed to reconnect to iDRAC after 5 attempts", 1, :red
|
@@ -47,7 +395,7 @@ module IDRAC
|
|
47
395
|
else
|
48
396
|
debug "Failed to reset iDRAC. Status code: #{response.status}", 1, :red
|
49
397
|
end
|
50
|
-
rescue
|
398
|
+
rescue
|
51
399
|
debug "Failed to reset iDRAC. Status code: #{response.status}", 1, :red
|
52
400
|
debug "Error response: #{response.body}", 2, :red
|
53
401
|
end
|
data/lib/idrac/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: idrac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Siegel
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: httparty
|
@@ -156,20 +155,14 @@ dependencies:
|
|
156
155
|
requirements:
|
157
156
|
- - ">="
|
158
157
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
160
|
-
- - "<"
|
161
|
-
- !ruby/object:Gem::Version
|
162
|
-
version: '8.1'
|
158
|
+
version: '7.0'
|
163
159
|
type: :runtime
|
164
160
|
prerelease: false
|
165
161
|
version_requirements: !ruby/object:Gem::Requirement
|
166
162
|
requirements:
|
167
163
|
- - ">="
|
168
164
|
- !ruby/object:Gem::Version
|
169
|
-
version: '
|
170
|
-
- - "<"
|
171
|
-
- !ruby/object:Gem::Version
|
172
|
-
version: '8.1'
|
165
|
+
version: '7.0'
|
173
166
|
- !ruby/object:Gem::Dependency
|
174
167
|
name: bundler
|
175
168
|
requirement: !ruby/object:Gem::Requirement
|
@@ -281,12 +274,12 @@ files:
|
|
281
274
|
- lib/idrac/version.rb
|
282
275
|
- lib/idrac/virtual_media.rb
|
283
276
|
- lib/idrac/web.rb
|
284
|
-
homepage:
|
277
|
+
homepage: https://github.com/buildio/idrac
|
285
278
|
licenses:
|
286
279
|
- MIT
|
287
280
|
metadata:
|
288
|
-
homepage_uri:
|
289
|
-
|
281
|
+
homepage_uri: https://github.com/buildio/idrac
|
282
|
+
source_code_uri: https://github.com/buildio/idrac
|
290
283
|
rdoc_options: []
|
291
284
|
require_paths:
|
292
285
|
- lib
|
@@ -294,15 +287,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
294
287
|
requirements:
|
295
288
|
- - ">="
|
296
289
|
- !ruby/object:Gem::Version
|
297
|
-
version:
|
290
|
+
version: 2.7.0
|
298
291
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
299
292
|
requirements:
|
300
293
|
- - ">="
|
301
294
|
- !ruby/object:Gem::Version
|
302
295
|
version: '0'
|
303
296
|
requirements: []
|
304
|
-
rubygems_version: 3.
|
305
|
-
signing_key:
|
297
|
+
rubygems_version: 3.6.7
|
306
298
|
specification_version: 4
|
307
299
|
summary: API Client for Dell iDRAC
|
308
300
|
test_files: []
|