idrac 0.1.38 → 0.1.41

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.
data/lib/idrac/jobs.rb ADDED
@@ -0,0 +1,212 @@
1
+ require 'json'
2
+ require 'colorize'
3
+
4
+ module IDRAC
5
+ module JobMethods
6
+ # Get a list of jobs
7
+ def jobs
8
+ response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs?$expand=*($levels=1)')
9
+
10
+ if response.status == 200
11
+ begin
12
+ jobs_data = JSON.parse(response.body)
13
+ puts "Jobs: #{jobs_data['Members'].count}"
14
+ if jobs_data['Members'].count > 0
15
+ puts "Job IDs:"
16
+ jobs_data["Members"].each do |job|
17
+ puts " #{job['Id']}"
18
+ end
19
+ end
20
+ return jobs_data
21
+ rescue JSON::ParserError
22
+ raise Error, "Failed to parse jobs response: #{response.body}"
23
+ end
24
+ else
25
+ raise Error, "Failed to get jobs. Status code: #{response.status}"
26
+ end
27
+ end
28
+
29
+ # Get detailed job information
30
+ def jobs_detail
31
+ response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs?$expand=*($levels=1)')
32
+
33
+ if response.status == 200
34
+ begin
35
+ jobs_data = JSON.parse(response.body)
36
+ jobs_data["Members"].each do |job|
37
+ puts "#{job['Id']} : #{job['JobState']} > #{job['Message']}"
38
+ end
39
+ return jobs_data
40
+ rescue JSON::ParserError
41
+ raise Error, "Failed to parse jobs detail response: #{response.body}"
42
+ end
43
+ else
44
+ raise Error, "Failed to get jobs detail. Status code: #{response.status}"
45
+ end
46
+ end
47
+
48
+ # Clear all jobs from the job queue
49
+ def clear_jobs!
50
+ # Get list of jobs
51
+ jobs_response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs?$expand=*($levels=1)')
52
+
53
+ if jobs_response.status == 200
54
+ begin
55
+ jobs_data = JSON.parse(jobs_response.body)
56
+ members = jobs_data["Members"]
57
+
58
+ # Delete each job individually
59
+ members.each.with_index do |job, i|
60
+ puts "Removing #{job['Id']} [#{i+1}/#{members.count}]"
61
+ delete_response = authenticated_request(:delete, "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/#{job['Id']}")
62
+
63
+ unless delete_response.status.between?(200, 299)
64
+ puts "Warning: Failed to delete job #{job['Id']}. Status code: #{delete_response.status}".yellow
65
+ end
66
+ end
67
+
68
+ puts "Successfully cleared all jobs".green
69
+ return true
70
+ rescue JSON::ParserError
71
+ raise Error, "Failed to parse jobs response: #{jobs_response.body}"
72
+ end
73
+ else
74
+ raise Error, "Failed to get jobs. Status code: #{jobs_response.status}"
75
+ end
76
+ end
77
+
78
+ # Force clear the job queue
79
+ def force_clear_jobs!
80
+ # Clear the job queue using force option which will also clear any pending data and restart processes
81
+ path = '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService/Actions/DellJobService.DeleteJobQueue'
82
+ payload = { "JobID" => "JID_CLEARALL_FORCE" }
83
+
84
+ response = authenticated_request(
85
+ :post,
86
+ path,
87
+ body: payload.to_json,
88
+ headers: { 'Content-Type' => 'application/json' }
89
+ )
90
+
91
+ if response.status.between?(200, 299)
92
+ puts "Successfully force-cleared job queue".green
93
+
94
+ # Monitor LC status until it's Ready
95
+ puts "Waiting for LC status to be Ready..."
96
+
97
+ retries = 12 # ~2 minutes with 10s sleep
98
+ while retries > 0
99
+ lc_response = authenticated_request(
100
+ :post,
101
+ '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus',
102
+ body: {}.to_json,
103
+ headers: { 'Content-Type': 'application/json' }
104
+ )
105
+
106
+ if lc_response.status.between?(200, 299)
107
+ begin
108
+ lc_data = JSON.parse(lc_response.body)
109
+ status = lc_data["LCStatus"]
110
+
111
+ if status == "Ready"
112
+ puts "LC Status is Ready".green
113
+ return true
114
+ end
115
+
116
+ puts "Current LC Status: #{status}. Waiting..."
117
+ rescue JSON::ParserError
118
+ puts "Failed to parse LC status response, will retry...".yellow
119
+ end
120
+ end
121
+
122
+ retries -= 1
123
+ sleep 10
124
+ end
125
+
126
+ puts "Warning: LC status did not reach Ready state within timeout".yellow
127
+ return true
128
+ else
129
+ error_message = "Failed to force-clear job queue. Status code: #{response.status}"
130
+
131
+ begin
132
+ error_data = JSON.parse(response.body)
133
+ error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
134
+ rescue
135
+ # Ignore JSON parsing errors
136
+ end
137
+
138
+ raise Error, error_message
139
+ end
140
+ end
141
+
142
+ # Wait for a job to complete
143
+ def wait_for_job(job_id)
144
+ # Job ID can be a job ID, path, or response hash from another request
145
+ job_path = if job_id.is_a?(Hash)
146
+ if job_id['headers'] && job_id['headers']['location']
147
+ job_id['headers']['location'].sub(/^\/redfish\/v1\//, '')
148
+ else
149
+ raise Error, "Invalid job hash, missing location header"
150
+ end
151
+ elsif job_id.to_s.start_with?('/redfish/v1/')
152
+ job_id.sub(/^\/redfish\/v1\//, '')
153
+ else
154
+ "Managers/iDRAC.Embedded.1/Jobs/#{job_id}"
155
+ end
156
+
157
+ puts "Waiting for job to complete: #{job_id}".light_cyan
158
+
159
+ retries = 36 # ~6 minutes with 10s sleep
160
+ while retries > 0
161
+ response = authenticated_request(:get, "/redfish/v1/#{job_path}")
162
+
163
+ if response.status == 200
164
+ begin
165
+ job_data = JSON.parse(response.body)
166
+ job_state = job_data["JobState"]
167
+
168
+ case job_state
169
+ when "Completed"
170
+ puts "Job completed successfully".green
171
+ return job_data
172
+ when "Failed"
173
+ puts "Job failed: #{job_data['Message']}".red
174
+ raise Error, "Job failed: #{job_data['Message']}"
175
+ when "CompletedWithErrors"
176
+ puts "Job completed with errors: #{job_data['Message']}".yellow
177
+ return job_data
178
+ end
179
+
180
+ puts "Job state: #{job_state}. Waiting...".yellow
181
+ rescue JSON::ParserError
182
+ puts "Failed to parse job status response, will retry...".yellow
183
+ end
184
+ else
185
+ puts "Failed to get job status. Status code: #{response.status}".red
186
+ end
187
+
188
+ retries -= 1
189
+ sleep 10
190
+ end
191
+
192
+ raise Error, "Timeout waiting for job to complete"
193
+ end
194
+
195
+ # Get system tasks
196
+ def tasks
197
+ response = authenticated_request(:get, '/redfish/v1/TaskService/Tasks')
198
+
199
+ if response.status == 200
200
+ begin
201
+ tasks_data = JSON.parse(response.body)
202
+ # "Tasks: #{tasks_data['Members'].count}", 0
203
+ return tasks_data['Members']
204
+ rescue JSON::ParserError
205
+ raise Error, "Failed to parse tasks response: #{response.body}"
206
+ end
207
+ else
208
+ raise Error, "Failed to get tasks. Status code: #{response.status}"
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,300 @@
1
+ require 'json'
2
+ require 'colorize'
3
+
4
+ module IDRAC
5
+ module LifecycleMethods
6
+ # Get the Lifecycle Controller status
7
+ def get_lifecycle_status
8
+ # Try first method (older iDRACs)
9
+ path1 = '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus'
10
+
11
+ begin
12
+ response = authenticated_request(
13
+ :post,
14
+ path1,
15
+ body: {}.to_json,
16
+ headers: { 'Content-Type' => 'application/json' }
17
+ )
18
+
19
+ if response.status.between?(200, 299)
20
+ begin
21
+ lc_data = JSON.parse(response.body)
22
+ puts "LC Status: #{lc_data['LCStatus']}".light_cyan
23
+ return lc_data
24
+ rescue JSON::ParserError
25
+ # Fall through to alternative method
26
+ end
27
+ end
28
+ rescue => e
29
+ # Fall through to alternative method
30
+ end
31
+
32
+ # Try alternative method (newer iDRACs)
33
+ path2 = '/redfish/v1/Managers/iDRAC.Embedded.1/Attributes'
34
+
35
+ begin
36
+ response = authenticated_request(:get, path2)
37
+
38
+ if response.status.between?(200, 299)
39
+ begin
40
+ attributes_data = JSON.parse(response.body)
41
+
42
+ if attributes_data["Attributes"] && attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
43
+ lifecycle_state = attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
44
+ puts "Lifecycle Controller state: #{lifecycle_state}".light_cyan
45
+ return { "LCStatus" => lifecycle_state }
46
+ end
47
+ rescue JSON::ParserError
48
+ # Fall through to final error
49
+ end
50
+ end
51
+ rescue => e
52
+ # Fall through to final error
53
+ end
54
+
55
+ # If we get here, try one last approach - try to get iDRAC status
56
+ begin
57
+ response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1')
58
+
59
+ if response.status.between?(200, 299)
60
+ begin
61
+ data = JSON.parse(response.body)
62
+ status = data["Status"] && data["Status"]["State"]
63
+ if status
64
+ puts "iDRAC State: #{status}".light_cyan
65
+ puts "Note: Could not retrieve direct LC status, showing iDRAC status instead".yellow
66
+ return { "iDRACStatus" => status }
67
+ end
68
+ rescue JSON::ParserError
69
+ # Fall through to final error
70
+ end
71
+ end
72
+ rescue => e
73
+ # Fall through to final error
74
+ end
75
+
76
+ # If we reached here, all methods failed
77
+ puts "Unable to retrieve Lifecycle Controller status through any available method".red
78
+ raise Error, "Failed to get Lifecycle Controller status through any available method"
79
+ end
80
+
81
+ # Check if the Lifecycle Controller is enabled
82
+ def get_idrac_lifecycle_status
83
+ # Use the DellLCService GetRemoteServicesAPIStatus
84
+ path = '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus'
85
+
86
+ response = authenticated_request(
87
+ :post,
88
+ path,
89
+ body: {}.to_json,
90
+ headers: { 'Content-Type' => 'application/json' }
91
+ )
92
+
93
+ if response.status.between?(200, 299)
94
+ begin
95
+ lc_data = JSON.parse(response.body)
96
+ status = lc_data["LCStatus"]
97
+
98
+ debug "LC Status: #{status}", 1
99
+
100
+ # Get the LCReplication status
101
+ attributes_path = "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes"
102
+ attributes_response = authenticated_request(:get, attributes_path)
103
+
104
+ if attributes_response.status == 200
105
+ begin
106
+ attributes_data = JSON.parse(attributes_response.body)
107
+ lc_replication = attributes_data["Attributes"]["ServiceModule.1.LCLReplication"]
108
+
109
+ debug "ServiceModule.1.LCLReplication: #{lc_replication}", 1
110
+
111
+ is_enabled = lc_replication == "Enabled"
112
+
113
+ puts "Lifecycle Controller replication is #{is_enabled ? 'enabled' : 'disabled'}".light_cyan
114
+ puts "Lifecycle Controller status: #{status}".light_cyan
115
+ return is_enabled
116
+ rescue => e
117
+ debug "Error parsing attributes: #{e.message}", 1
118
+ end
119
+ end
120
+
121
+ # If we can't determine from attributes, just return if LC is Ready
122
+ is_ready = status == "Ready"
123
+ puts "Lifecycle Controller is #{is_ready ? 'Ready' : status}".light_cyan
124
+ return is_ready
125
+ rescue JSON::ParserError
126
+ raise Error, "Failed to parse Lifecycle Controller status response: #{response.body}"
127
+ end
128
+ else
129
+ raise Error, "Failed to get Lifecycle Controller status. Status code: #{response.status}"
130
+ end
131
+ end
132
+
133
+ # Set the Lifecycle Controller status (enable/disable)
134
+ def set_idrac_lifecycle_status(status)
135
+ enabled = !!status # Convert to boolean
136
+
137
+ debug "Setting Lifecycle Controller status to #{enabled ? 'enabled' : 'disabled'}", 1
138
+
139
+ # Use the attributes method to set the ServiceModule.1.LCLReplication
140
+ path = "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes"
141
+
142
+ # Create the payload with the attribute we want to modify
143
+ payload = {
144
+ "Attributes": {
145
+ "ServiceModule.1.LCLReplication": enabled ? "Enabled" : "Disabled"
146
+ }
147
+ }
148
+
149
+ debug "Using attributes endpoint: #{path}", 1
150
+ debug "Payload: #{payload.inspect}", 1
151
+
152
+ begin
153
+ response = authenticated_request(
154
+ :patch,
155
+ path,
156
+ body: payload.to_json,
157
+ headers: { 'Content-Type' => 'application/json' }
158
+ )
159
+
160
+ debug "Response status: #{response.status}", 1
161
+ debug "Response body: #{response.body}", 2 if response.body
162
+
163
+ if response.status.between?(200, 299)
164
+ puts "Successfully #{enabled ? 'enabled' : 'disabled'} Lifecycle Controller".green
165
+ return true
166
+ else
167
+ error_message = "Failed to set Lifecycle Controller status. Status code: #{response.status}"
168
+
169
+ # Print the full response body for debugging
170
+ puts "Full error response body:".red
171
+ puts response.body.inspect.red
172
+
173
+ begin
174
+ error_data = JSON.parse(response.body)
175
+ puts "Extended error information:".red if error_data['@Message.ExtendedInfo']
176
+
177
+ if error_data['error'] && error_data['error']['message']
178
+ error_message += ", Message: #{error_data['error']['message']}"
179
+ end
180
+
181
+ if error_data['@Message.ExtendedInfo']
182
+ error_data['@Message.ExtendedInfo'].each do |info|
183
+ puts " Message: #{info['Message']}".red
184
+ puts " Resolution: #{info['Resolution']}".yellow if info['Resolution']
185
+ puts " Severity: #{info['Severity']}".yellow if info['Severity']
186
+ puts " MessageId: #{info['MessageId']}".yellow if info['MessageId']
187
+ end
188
+
189
+ if error_data['@Message.ExtendedInfo'].first
190
+ error_message += ", Message: #{error_data['@Message.ExtendedInfo'].first['Message']}"
191
+ error_message += ", Resolution: #{error_data['@Message.ExtendedInfo'].first['Resolution']}" if error_data['@Message.ExtendedInfo'].first['Resolution']
192
+ end
193
+ end
194
+ rescue => e
195
+ debug "Error parsing response: #{e.message}", 1
196
+ # Ignore JSON parsing errors
197
+ end
198
+
199
+ raise Error, error_message
200
+ end
201
+ rescue => e
202
+ debug "Error in request: #{e.message}", 1
203
+ raise Error, "Failed to set Lifecycle Controller status: #{e.message}"
204
+ end
205
+ end
206
+
207
+ # Ensure the Lifecycle Controller is enabled
208
+ def ensure_lifecycle_controller!
209
+ if !get_idrac_lifecycle_status
210
+ puts "Lifecycle Controller is disabled, enabling...".yellow
211
+ set_idrac_lifecycle_status(true)
212
+
213
+ # Verify it was enabled
214
+ if !get_idrac_lifecycle_status
215
+ raise Error, "Failed to enable Lifecycle Controller"
216
+ end
217
+
218
+ puts "Lifecycle Controller successfully enabled".green
219
+ else
220
+ puts "Lifecycle Controller is already enabled".green
221
+ end
222
+
223
+ return true
224
+ end
225
+
226
+ # Clear the Lifecycle log
227
+ def clear_lifecycle!
228
+ path = '/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/Actions/DellLCService.SystemErase'
229
+ payload = { "Component": ["LCData"] }
230
+
231
+ response = authenticated_request(
232
+ :post,
233
+ path,
234
+ body: payload.to_json,
235
+ headers: { 'Content-Type' => 'application/json' }
236
+ )
237
+
238
+ if response.status.between?(200, 299)
239
+ puts "Lifecycle log cleared".green
240
+ return true
241
+ else
242
+ puts "Failed to clear Lifecycle log".red
243
+
244
+ error_message = "Failed to clear Lifecycle log. Status code: #{response.status}"
245
+
246
+ begin
247
+ error_data = JSON.parse(response.body)
248
+ error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
249
+ rescue
250
+ # Ignore JSON parsing errors
251
+ end
252
+
253
+ raise Error, error_message
254
+ end
255
+ end
256
+
257
+ # Get the system event logs
258
+ def get_system_event_logs
259
+ path = 'Managers/iDRAC.Embedded.1/Logs/Sel?$expand=*($levels=1)'
260
+
261
+ response = authenticated_request(:get, path)
262
+
263
+ if response.status == 200
264
+ begin
265
+ logs_data = JSON.parse(response.body)
266
+ return logs_data
267
+ rescue JSON::ParserError
268
+ raise Error, "Failed to parse system event logs response: #{response.body}"
269
+ end
270
+ else
271
+ raise Error, "Failed to get system event logs. Status code: #{response.status}"
272
+ end
273
+ end
274
+
275
+ # Clear the system event logs
276
+ def clear_system_event_logs!
277
+ path = 'Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog'
278
+
279
+ response = authenticated_request(:post, path, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
280
+
281
+ if response.status.between?(200, 299)
282
+ puts "System Event Logs cleared".green
283
+ return true
284
+ else
285
+ puts "Failed to clear System Event Logs".red
286
+
287
+ error_message = "Failed to clear System Event Logs. Status code: #{response.status}"
288
+
289
+ begin
290
+ error_data = JSON.parse(response.body)
291
+ error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
292
+ rescue
293
+ # Ignore JSON parsing errors
294
+ end
295
+
296
+ raise Error, error_message
297
+ end
298
+ end
299
+ end
300
+ end