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.
- checksums.yaml +4 -4
- data/README.md +76 -14
- data/bin/idrac +246 -89
- data/lib/idrac/client.rb +72 -382
- data/lib/idrac/firmware.rb +26 -2
- data/lib/idrac/jobs.rb +212 -0
- data/lib/idrac/lifecycle.rb +300 -0
- data/lib/idrac/power.rb +195 -0
- data/lib/idrac/session.rb +717 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/web.rb +187 -0
- data/lib/idrac.rb +29 -7
- metadata +8 -4
- data/lib/idrac/screenshot.rb +0 -49
data/lib/idrac/power.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module IDRAC
|
5
|
+
module PowerMethods
|
6
|
+
def power_on(wait: true)
|
7
|
+
# Login to iDRAC if needed
|
8
|
+
login unless @session_id
|
9
|
+
|
10
|
+
puts "Powering on server...".light_cyan
|
11
|
+
|
12
|
+
# Check current power state first
|
13
|
+
current_state = get_power_state rescue "Unknown"
|
14
|
+
if current_state == "On"
|
15
|
+
puts "Server is already powered ON.".yellow
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
|
19
|
+
# Send power on command (Reset with ResetType=On)
|
20
|
+
path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
|
21
|
+
payload = { "ResetType" => "On" }
|
22
|
+
|
23
|
+
tries = 10
|
24
|
+
while tries > 0
|
25
|
+
response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
26
|
+
|
27
|
+
case response.status
|
28
|
+
when 200, 204
|
29
|
+
puts "Server power on command sent successfully".green
|
30
|
+
break
|
31
|
+
when 409
|
32
|
+
begin
|
33
|
+
error_data = JSON.parse(response.body)
|
34
|
+
if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] &&
|
35
|
+
error_data["error"]["@Message.ExtendedInfo"].any? { |m| m["Message"] =~ /Server is already powered ON/ }
|
36
|
+
puts "Server is already powered ON.".yellow
|
37
|
+
return false
|
38
|
+
else
|
39
|
+
raise Error, "Failed to power on: #{error_data.inspect}"
|
40
|
+
end
|
41
|
+
rescue JSON::ParserError
|
42
|
+
raise Error, "Failed to power on with status 409: #{response.body}"
|
43
|
+
end
|
44
|
+
when 500
|
45
|
+
puts "[iDRAC 500] Server is busy...".red
|
46
|
+
tries -= 1
|
47
|
+
puts "Retrying... #{tries}/10".yellow if tries > 0
|
48
|
+
sleep 10
|
49
|
+
else
|
50
|
+
raise Error, "Unknown response code #{response.status}: #{response.body}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
raise Error, "Failed to power on after 10 retries" if tries <= 0
|
55
|
+
|
56
|
+
# Wait for power state change if requested
|
57
|
+
wait_for_power_state(target_state: "On", tries: 10) if wait
|
58
|
+
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
|
62
|
+
def power_off(wait: true, kind: "ForceOff")
|
63
|
+
# Login to iDRAC if needed
|
64
|
+
login unless @session_id
|
65
|
+
|
66
|
+
puts "Powering off server...".light_cyan
|
67
|
+
|
68
|
+
# Check current power state first
|
69
|
+
current_state = get_power_state rescue "Unknown"
|
70
|
+
if current_state == "Off"
|
71
|
+
puts "Server is already powered OFF.".yellow
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
|
75
|
+
# Send power off command
|
76
|
+
path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
|
77
|
+
payload = { "ResetType" => kind }
|
78
|
+
|
79
|
+
response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
80
|
+
|
81
|
+
case response.status
|
82
|
+
when 200, 204
|
83
|
+
puts "Server power off command sent successfully".green
|
84
|
+
when 409
|
85
|
+
# Conflict -- Server is already off
|
86
|
+
begin
|
87
|
+
error_data = JSON.parse(response.body)
|
88
|
+
if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] &&
|
89
|
+
error_data["error"]["@Message.ExtendedInfo"].any? { |m| m["Message"] =~ /Server is already powered OFF/ }
|
90
|
+
puts "Server is already powered OFF.".yellow
|
91
|
+
return false
|
92
|
+
else
|
93
|
+
raise Error, "Failed to power off: #{error_data.inspect}"
|
94
|
+
end
|
95
|
+
rescue JSON::ParserError
|
96
|
+
raise Error, "Failed to power off with status 409: #{response.body}"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
error_message = "Failed to power off server. Status code: #{response.status}"
|
100
|
+
begin
|
101
|
+
error_data = JSON.parse(response.body)
|
102
|
+
error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
|
103
|
+
rescue
|
104
|
+
# Ignore JSON parsing errors
|
105
|
+
end
|
106
|
+
raise Error, error_message
|
107
|
+
end
|
108
|
+
|
109
|
+
# Wait for power state change if requested
|
110
|
+
if wait
|
111
|
+
success = wait_for_power_state(target_state: "Off", tries: 6)
|
112
|
+
|
113
|
+
# If graceful shutdown failed, try force shutdown
|
114
|
+
if !success && kind != "ForceOff"
|
115
|
+
return power_off(wait: wait, kind: "ForceOff")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
return true
|
120
|
+
end
|
121
|
+
|
122
|
+
def reboot
|
123
|
+
# Login to iDRAC if needed
|
124
|
+
login unless @session_id
|
125
|
+
|
126
|
+
puts "Rebooting server...".light_cyan
|
127
|
+
|
128
|
+
# Send reboot command (Reset with ResetType=ForceRestart)
|
129
|
+
path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
|
130
|
+
payload = { "ResetType" => "ForceRestart" }
|
131
|
+
|
132
|
+
response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
133
|
+
|
134
|
+
if response.status >= 200 && response.status < 300
|
135
|
+
puts "Server reboot command sent successfully".green
|
136
|
+
return true
|
137
|
+
else
|
138
|
+
error_message = "Failed to reboot server. Status code: #{response.status}"
|
139
|
+
begin
|
140
|
+
error_data = JSON.parse(response.body)
|
141
|
+
error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
|
142
|
+
rescue
|
143
|
+
# Ignore JSON parsing errors
|
144
|
+
end
|
145
|
+
|
146
|
+
raise Error, error_message
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_power_state
|
151
|
+
# Login to iDRAC if needed
|
152
|
+
login unless @session_id
|
153
|
+
|
154
|
+
# Get system information
|
155
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1?$select=PowerState")
|
156
|
+
|
157
|
+
if response.status == 200
|
158
|
+
begin
|
159
|
+
system_data = JSON.parse(response.body)
|
160
|
+
return system_data["PowerState"]
|
161
|
+
rescue JSON::ParserError
|
162
|
+
raise Error, "Failed to parse power state response: #{response.body}"
|
163
|
+
end
|
164
|
+
else
|
165
|
+
raise Error, "Failed to get power state. Status code: #{response.status}"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def wait_for_power_state(target_state:, tries: 6)
|
172
|
+
retry_count = tries
|
173
|
+
|
174
|
+
while retry_count > 0
|
175
|
+
begin
|
176
|
+
current_state = get_power_state
|
177
|
+
|
178
|
+
return true if current_state == target_state
|
179
|
+
|
180
|
+
puts "Waiting for power #{target_state == 'On' ? 'on' : 'off'}...".yellow
|
181
|
+
puts "Current state: #{current_state}"
|
182
|
+
retry_count -= 1
|
183
|
+
sleep 8
|
184
|
+
rescue => e
|
185
|
+
puts "Error checking power state: #{e.message}".red
|
186
|
+
retry_count -= 1
|
187
|
+
sleep 5
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
puts "Failed to reach power state #{target_state}".red
|
192
|
+
return false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|