lib/idrac/power.rb
require 'json' require 'colorize' module IDRAC module Power def power_on(wait: true) # Login to iDRAC if needed login unless @session_id puts "Powering on server...".light_cyan # Check current power state first current_state = get_power_state rescue "Unknown" if current_state == "On" puts "Server is already powered ON.".yellow return false end # Send power on command (Reset with ResetType=On) path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" payload = { "ResetType" => "On" } tries = 10 while tries > 0 response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' }) case response.status when 200, 204 puts "Server power on command sent successfully".green break when 409 begin error_data = JSON.parse(response.body) if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] && error_data["error"]["@Message.ExtendedInfo"].any? { |m| m["Message"] =~ /Server is already powered ON/ } puts "Server is already powered ON.".yellow return false else raise Error, "Failed to power on: #{error_data.inspect}" end rescue JSON::ParserError raise Error, "Failed to power on with status 409: #{response.body}" end when 500 puts "[iDRAC 500] Server is busy...".red tries -= 1 puts "Retrying... #{tries}/10".yellow if tries > 0 sleep 10 else raise Error, "Unknown response code #{response.status}: #{response.body}" end end raise Error, "Failed to power on after 10 retries" if tries <= 0 # Wait for power state change if requested wait_for_power_state(target_state: "On", tries: 10) if wait return true end def power_off(wait: true, kind: "ForceOff") # Login to iDRAC if needed login unless @session_id puts "Powering off server...".light_cyan # Check current power state first current_state = get_power_state rescue "Unknown" if current_state == "Off" puts "Server is already powered OFF.".yellow return false end # Send power off command path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" payload = { "ResetType" => kind } response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' }) case response.status when 200, 204 puts "Server power off command sent successfully".green when 409 # Conflict -- Server is already off begin error_data = JSON.parse(response.body) if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] && error_data["error"]["@Message.ExtendedInfo"].any? { |m| m["Message"] =~ /Server is already powered OFF/ } puts "Server is already powered OFF.".yellow return false else raise Error, "Failed to power off: #{error_data.inspect}" end rescue JSON::ParserError raise Error, "Failed to power off with status 409: #{response.body}" end else error_message = "Failed to power off server. Status code: #{response.status}" begin error_data = JSON.parse(response.body) error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message'] rescue # Ignore JSON parsing errors end raise Error, error_message end # Wait for power state change if requested if wait success = wait_for_power_state(target_state: "Off", tries: 6) # If graceful shutdown failed, try force shutdown if !success && kind != "ForceOff" return power_off(wait: wait, kind: "ForceOff") end end return true end def reboot # Login to iDRAC if needed login unless @session_id puts "Rebooting server...".light_cyan # Send reboot command (Reset with ResetType=ForceRestart) path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" payload = { "ResetType" => "ForceRestart" } response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' }) if response.status >= 200 && response.status < 300 puts "Server reboot command sent successfully".green return true else error_message = "Failed to reboot server. Status code: #{response.status}" begin error_data = JSON.parse(response.body) error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message'] rescue # Ignore JSON parsing errors end raise Error, error_message end end def get_power_state # Login to iDRAC if needed login unless @session_id # Get system information response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1?$select=PowerState") if response.status == 200 begin system_data = JSON.parse(response.body) return system_data["PowerState"] rescue JSON::ParserError raise Error, "Failed to parse power state response: #{response.body}" end else raise Error, "Failed to get power state. Status code: #{response.status}" end end def get_power_usage_watts # Login to iDRAC if needed login unless @session_id response = authenticated_request(:get, "/redfish/v1/Chassis/System.Embedded.1/Power") if response.status == 200 begin data = JSON.parse(response.body) watts = data["PowerControl"][0]["PowerConsumedWatts"] # puts "Power usage: #{watts} watts".light_cyan return watts.to_f rescue JSON::ParserError raise Error, "Failed to parse power usage response: #{response.body}" end else error_message = "Failed to get power usage. Status code: #{response.status}" begin error_data = JSON.parse(response.body) error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message'] rescue # Ignore JSON parsing errors end raise Error, error_message end end private def wait_for_power_state(target_state:, tries: 6) retry_count = tries while retry_count > 0 begin current_state = get_power_state return true if current_state == target_state puts "Waiting for power #{target_state == 'On' ? 'on' : 'off'}...".yellow puts "Current state: #{current_state}" retry_count -= 1 sleep 8 rescue => e puts "Error checking power state: #{e.message}".red retry_count -= 1 sleep 5 end end puts "Failed to reach power state #{target_state}".red return false end end end