idrac 0.5.10 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1097b98fc3b31d5e19268ca31db441d03d791fdcca96a6d85418badad0db534f
4
- data.tar.gz: 6132fef1177ce26aef2045c3cecf1c81625ba09f0beaa6387126b0b1cb850d3f
3
+ metadata.gz: 5c77b78a7bad9201ed36f9570e2d0d39dc57559964d736648fdcd3f34e46fded
4
+ data.tar.gz: 0e5656537537fac5cbc02cc72c5c23e174c30d76c90d3cc80b2dd4da36d20ec4
5
5
  SHA512:
6
- metadata.gz: 67974993e8fe893654a3549b500f018bed2796b476c78cca99894ec003584b72eeb14b7dd6dfd7c0029f47acf17d7b454eed7cf5fd850dcf1a36ddc57c474a96
7
- data.tar.gz: 6cbfe56bce022b0cd2ae1cc2c74a6fc644422b323edefd913b819b1dd49f702cb7134ffbd8e03cf4d2b489d587548b46623f1188e6daaec2b3acbe95ebbff415
6
+ metadata.gz: f89101dedb96aeabd91eb5c210231d94500f65e41bd85c497a76c23478f658170d30ab11ba1ece65d113ae66a8736dd15ad6d4d44ecddf8e8da2ae3f6cd570d7
7
+ data.tar.gz: 7a072578dd6b178bc59c0ac09a66cd5752d1dd62d530d65c964aa624cf209df5bb1ed5189045ca04e3b4cab880d26646d3b045364aa352cde5fae3cca97dc6ff
data/README.md CHANGED
@@ -171,23 +171,26 @@ client.clear_lifecycle!
171
171
  # Clear System Event Logs
172
172
  client.clear_system_event_logs!
173
173
 
174
- # Working with RecursiveOpenStruct objects
175
- # Many methods return data as RecursiveOpenStruct objects for easier property access
174
+ # Working with hash objects
175
+ # Methods return data as Ruby hashes with string keys for consistent access
176
+
177
+ # Working with system components
178
+ # Methods return data as Ruby hashes with string keys for consistent access
176
179
 
177
180
  # Get memory information
178
181
  memory_modules = client.memory
179
182
  memory_modules.each do |dimm|
180
- # Access properties directly as methods
181
- puts "#{dimm.name}: #{dimm.capacity_bytes / (1024**3)}GB, Speed: #{dimm.speed_mhz}MHz"
183
+ # Access properties via string keys
184
+ puts "#{dimm["name"]}: #{dimm["capacity_bytes"] / (1024**3)}GB, Speed: #{dimm["speed_mhz"]}MHz"
182
185
  end
183
186
 
184
187
  # Get storage information
185
188
  controller = client.controller
186
189
  volumes = client.volumes(controller)
187
190
  volumes.each do |volume|
188
- # Access properties via dot notation
189
- puts "#{volume.name} (#{volume.raid_level}): #{volume.capacity_bytes / (1024**3)}GB"
190
- puts " Health: #{volume.health}, FastPath: #{volume.fastpath}"
191
+ # Access properties via string keys
192
+ puts "#{volume["name"]} (#{volume["raid_level"]}): #{volume["capacity_bytes"] / (1024**3)}GB"
193
+ puts " Health: #{volume["health"]}, FastPath: #{volume["fastpath"]}"
191
194
  end
192
195
 
193
196
  # Create a client with auto_delete_sessions disabled
data/bin/idrac CHANGED
@@ -581,17 +581,17 @@ module IDRAC
581
581
  puts "-" * 80
582
582
 
583
583
  controllers.each do |controller|
584
- puts "Controller: #{controller[:name]}".cyan.bold
585
- puts " Model: #{controller[:model]}"
586
- puts " Status: #{controller[:status]}"
587
- puts " Drives: #{controller[:drives_count]}"
588
- puts " Firmware: #{controller[:firmware_version]}"
589
- puts " Type: #{controller[:controller_type]}"
590
- puts " PCI Slot: #{controller[:pci_slot]}"
584
+ puts "Controller: #{controller.name}".cyan.bold
585
+ puts " Model: #{controller.model}"
586
+ puts " Status: #{controller.status}"
587
+ puts " Drives: #{controller.drives_count}"
588
+ puts " Firmware: #{controller.firmware_version}"
589
+ puts " Type: #{controller.controller_type}"
590
+ puts " PCI Slot: #{controller.pci_slot}"
591
591
 
592
- if controller[:encryption_capability]
593
- puts " Encryption: #{controller[:encryption_capability]}"
594
- puts " Encryption Mode: #{controller[:encryption_mode] || 'Disabled'}"
592
+ if controller.encryption_capability
593
+ puts " Encryption: #{controller.encryption_capability}"
594
+ puts " Encryption Mode: #{controller.encryption_mode || 'Disabled'}"
595
595
  end
596
596
 
597
597
  puts
@@ -675,7 +675,7 @@ module IDRAC
675
675
  confirmation = $stdin.gets.chomp.downcase
676
676
 
677
677
  if confirmation == 'y'
678
- client.create_virtual_disk(controller.odata_Id, drives, name: options[:name], raid_type: options[:raid])
678
+ client.create_virtual_disk(controller.odata_id, drives, name: options[:name], raid_type: options[:raid])
679
679
  puts "Volume created successfully".green
680
680
  else
681
681
  puts "Operation cancelled".yellow
data/idrac.gemspec CHANGED
@@ -41,7 +41,6 @@ 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 "recursive-open-struct", "~> 1.1", ">= 1.1.0"
45
44
  spec.add_dependency "activesupport", "~> 6.0"
46
45
 
47
46
  # Development dependencies
data/lib/idrac/jobs.rb CHANGED
@@ -3,21 +3,16 @@ require 'colorize'
3
3
 
4
4
  module IDRAC
5
5
  module Jobs
6
- # Get a list of jobs
6
+ # Summarize jobs
7
7
  def jobs
8
8
  response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs?$expand=*($levels=1)')
9
9
 
10
10
  if response.status == 200
11
11
  begin
12
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
13
+ { completed_count: jobs_data["Members"].select { |j| j["JobState"] == "Completed" }.count,
14
+ incomplete_count: jobs_data["Members"].select { |j| j["JobState"] != "Completed" }.count,
15
+ total_count: jobs_data["Members"].count }
21
16
  rescue JSON::ParserError
22
17
  raise Error, "Failed to parse jobs response: #{response.body}"
23
18
  end
data/lib/idrac/license.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module IDRAC
2
2
  module License
3
3
  # Gets the license information from the iDRAC
4
- # @return [RecursiveOpenStruct] License details
4
+ # @return [Hash] License details
5
5
  def license_info
6
6
  response = authenticated_request(:get, "/redfish/v1/LicenseService/Licenses")
7
7
  if response.status != 200
@@ -32,7 +32,7 @@ module IDRAC
32
32
  license_details = JSON.parse(license_response.body)
33
33
  debug "License details: #{license_details}", 2
34
34
 
35
- return RecursiveOpenStruct.new(license_details, recurse_over_arrays: true)
35
+ return license_details
36
36
  end
37
37
 
38
38
  # Extracts the iDRAC version from the license description
@@ -43,15 +43,15 @@ module IDRAC
43
43
 
44
44
  # Check the Description field, which often contains the version
45
45
  # Example: "iDRAC9 Enterprise License"
46
- if license.Description && license.Description.match(/iDRAC(\d+)/i)
47
- version = license.Description.match(/iDRAC(\d+)/i)[1].to_i
46
+ if license["Description"]&.match(/iDRAC(\d+)/i)
47
+ version = license["Description"].match(/iDRAC(\d+)/i)[1].to_i
48
48
  debug "Found license version from Description: #{version}", 1
49
49
  return version
50
50
  end
51
51
 
52
52
  # Try alternative fields if Description didn't work
53
- if license.Name && license.Name.match(/iDRAC(\d+)/i)
54
- version = license.Name.match(/iDRAC(\d+)/i)[1].to_i
53
+ if license["Name"]&.match(/iDRAC(\d+)/i)
54
+ version = license["Name"].match(/iDRAC(\d+)/i)[1].to_i
55
55
  debug "Found license version from Name: #{version}", 1
56
56
  return version
57
57
  end
@@ -148,14 +148,21 @@ module IDRAC
148
148
 
149
149
  # Get the system event logs
150
150
  def get_system_event_logs
151
- path = 'Managers/iDRAC.Embedded.1/Logs/Sel?$expand=*($levels=1)'
151
+ path = '/redfish/v1/Managers/iDRAC.Embedded.1/Logs/Sel?$expand=*($levels=1)'
152
152
 
153
153
  response = authenticated_request(:get, path)
154
154
 
155
155
  if response.status == 200
156
156
  begin
157
- logs_data = JSON.parse(response.body)
158
- return logs_data
157
+ data = JSON.parse(response.body)['Members'].map do |entry|
158
+ {
159
+ id: entry['Id'],
160
+ created: entry['Created'],
161
+ message: entry['Message'],
162
+ severity: entry['Severity']
163
+ }
164
+ end
165
+ return data # RecursiveOpenStruct.new(data, recurse_over_arrays: true)
159
166
  rescue JSON::ParserError
160
167
  raise Error, "Failed to parse system event logs response: #{response.body}"
161
168
  end
@@ -166,7 +173,7 @@ module IDRAC
166
173
 
167
174
  # Clear the system event logs
168
175
  def clear_system_event_logs!
169
- path = 'Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog'
176
+ path = '/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog'
170
177
 
171
178
  response = authenticated_request(:post, path, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
172
179
 
data/lib/idrac/storage.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'json'
2
2
  require 'colorize'
3
- require 'recursive-open-struct'
4
3
 
5
4
  module IDRAC
6
5
  module Storage
@@ -10,24 +9,24 @@ module IDRAC
10
9
  controller_list = controllers
11
10
 
12
11
  puts "Controllers".green
13
- controller_list.each { |c| puts "#{c.name} > #{c.drives_count}" }
12
+ controller_list.each { |c| puts "#{c["name"]} > #{c["drives_count"]}" }
14
13
 
15
14
  puts "Drives".green
16
15
  controller_list.each do |c|
17
- puts "Storage: #{c.name} > #{c.status} > #{c.drives_count}"
16
+ puts "Storage: #{c["name"]} > #{c["status"]} > #{c["drives_count"]}"
18
17
  end
19
18
 
20
19
  # Find the controller with the most drives (usually the PERC)
21
- controller_info = controller_list.max_by { |c| c.drives_count }
20
+ controller_info = controller_list.max_by { |c| c["drives_count"] }
22
21
 
23
- if controller_info.name =~ /PERC/
24
- puts "Found #{controller_info.name}".green
22
+ if controller_info["name"] =~ /PERC/
23
+ puts "Found #{controller_info["name"]}".green
25
24
  else
26
- puts "Found #{controller_info.name} but continuing...".yellow
25
+ puts "Found #{controller_info["name"]} but continuing...".yellow
27
26
  end
28
27
 
29
28
  # Return the raw controller data
30
- controller_info.raw
29
+ controller_info["raw"]
31
30
  end
32
31
 
33
32
  # Get all storage controllers and return them as an array
@@ -38,26 +37,25 @@ module IDRAC
38
37
  begin
39
38
  data = JSON.parse(response.body)
40
39
 
41
- # Transform and return all controllers as an array of RecursiveOpenStruct objects with consistent keys
40
+ # Transform and return all controllers as an array of hashes with string keys
42
41
  controllers = data["Members"].map do |controller|
43
- controller_data = {
44
- name: controller["Name"],
45
- model: controller["Model"],
46
- drives_count: controller["Drives"].size,
47
- status: controller["Status"]["Health"] || "N/A",
48
- firmware_version: controller.dig("StorageControllers", 0, "FirmwareVersion"),
49
- encryption_mode: controller.dig("Oem", "Dell", "DellController", "EncryptionMode"),
50
- encryption_capability: controller.dig("Oem", "Dell", "DellController", "EncryptionCapability"),
51
- controller_type: controller.dig("Oem", "Dell", "DellController", "ControllerType"),
52
- pci_slot: controller.dig("Oem", "Dell", "DellController", "PCISlot"),
53
- raw: controller,
54
- odata_id: controller["@odata.id"]
42
+ {
43
+ "name" => controller["Name"],
44
+ "model" => controller["Model"],
45
+ "drives_count" => controller["Drives"].size,
46
+ "status" => controller.dig("Status", "Health") || "N/A",
47
+ "firmware_version" => controller.dig("StorageControllers", 0, "FirmwareVersion"),
48
+ "encryption_mode" => controller.dig("Oem", "Dell", "DellController", "EncryptionMode"),
49
+ "encryption_capability" => controller.dig("Oem", "Dell", "DellController", "EncryptionCapability"),
50
+ "controller_type" => controller.dig("Oem", "Dell", "DellController", "ControllerType"),
51
+ "pci_slot" => controller.dig("Oem", "Dell", "DellController", "PCISlot"),
52
+ "raw" => controller,
53
+ "volumes_odata_id" => controller.dig("Volumes", "@odata.id"),
54
+ "@odata.id" => controller["@odata.id"]
55
55
  }
56
-
57
- RecursiveOpenStruct.new(controller_data, recurse_over_arrays: true)
58
56
  end
59
57
 
60
- return controllers.sort_by { |c| c.name }
58
+ return controllers.sort_by { |c| c["name"] }
61
59
  rescue JSON::ParserError
62
60
  raise Error, "Failed to parse controllers response: #{response.body}"
63
61
  end
@@ -66,23 +64,12 @@ module IDRAC
66
64
  end
67
65
  end
68
66
 
69
- # Check if controller supports encryption
70
- def controller_encryption_capable?(controller)
71
- return false unless controller
72
- controller.dig("Oem", "Dell", "DellController", "EncryptionCapability") =~ /localkey/i
73
- end
74
-
75
- # Check if controller encryption is enabled
76
- def controller_encryption_enabled?(controller)
77
- return false unless controller
78
- controller.dig("Oem", "Dell", "DellController", "EncryptionMode") =~ /localkey/i
79
- end
80
-
81
67
  # Get information about physical drives
82
68
  def drives(controller)
83
69
  raise Error, "Controller not provided" unless controller
84
70
 
85
- controller_path = controller["@odata.id"].split("v1/").last
71
+ odata_id_path = controller["@odata.id"] || controller["odata_id"]
72
+ controller_path = odata_id_path.split("v1/").last
86
73
  response = authenticated_request(:get, "/redfish/v1/#{controller_path}?$expand=*($levels=1)")
87
74
 
88
75
  if response.status == 200
@@ -91,29 +78,27 @@ module IDRAC
91
78
  drives = data["Drives"].map do |body|
92
79
  serial = body["SerialNumber"]
93
80
  serial = body["Identifiers"].first["DurableName"] if serial.blank?
94
- drive_data = {
95
- serial: serial,
96
- model: body["Model"],
97
- name: body["Name"],
98
- capacity_bytes: body["CapacityBytes"],
99
- health: body["Status"]["Health"] ? body["Status"]["Health"] : "N/A",
100
- speed_gbp: body["CapableSpeedGbs"],
101
- manufacturer: body["Manufacturer"],
102
- media_type: body["MediaType"],
103
- failure_predicted: body["FailurePredicted"],
104
- life_left_percent: body["PredictedMediaLifeLeftPercent"],
105
- certified: body.dig("Oem", "Dell", "DellPhysicalDisk", "Certified"),
106
- raid_status: body.dig("Oem", "Dell", "DellPhysicalDisk", "RaidStatus"),
107
- operation_name: body.dig("Oem", "Dell", "DellPhysicalDisk", "OperationName"),
108
- operation_progress: body.dig("Oem", "Dell", "DellPhysicalDisk", "OperationPercentCompletePercent"),
109
- encryption_ability: body["EncryptionAbility"],
110
- "@odata.id": body["@odata.id"]
81
+ {
82
+ "serial" => serial,
83
+ "model" => body["Model"],
84
+ "name" => body["Name"],
85
+ "capacity_bytes" => body["CapacityBytes"],
86
+ "health" => body.dig("Status", "Health") || "N/A",
87
+ "speed_gbp" => body["CapableSpeedGbs"],
88
+ "manufacturer" => body["Manufacturer"],
89
+ "media_type" => body["MediaType"],
90
+ "failure_predicted" => body["FailurePredicted"],
91
+ "life_left_percent" => body["PredictedMediaLifeLeftPercent"],
92
+ "certified" => body.dig("Oem", "Dell", "DellPhysicalDisk", "Certified"),
93
+ "raid_status" => body.dig("Oem", "Dell", "DellPhysicalDisk", "RaidStatus"),
94
+ "operation_name" => body.dig("Oem", "Dell", "DellPhysicalDisk", "OperationName"),
95
+ "operation_progress" => body.dig("Oem", "Dell", "DellPhysicalDisk", "OperationPercentCompletePercent"),
96
+ "encryption_ability" => body["EncryptionAbility"],
97
+ "@odata.id" => body["@odata.id"]
111
98
  }
112
-
113
- RecursiveOpenStruct.new(drive_data, recurse_over_arrays: true)
114
99
  end
115
100
 
116
- return drives.sort_by { |d| d.name }
101
+ return drives.sort_by { |d| d["name"] }
117
102
  rescue JSON::ParserError
118
103
  raise Error, "Failed to parse drives response: #{response.body}"
119
104
  end
@@ -128,9 +113,11 @@ module IDRAC
128
113
 
129
114
  puts "Volumes (e.g. Arrays)".green
130
115
 
131
- v = controller["Volumes"]
132
- path = v["@odata.id"].split("v1/").last
133
- response = authenticated_request(:get, "/redfish/v1/#{path}?$expand=*($levels=1)")
116
+ odata_id_path = controller["volumes_odata_id"]
117
+ if odata_id_path.nil?
118
+ raise Error, "No volumes_odata_id found in controller data. Make sure the controller is properly initialized."
119
+ end
120
+ response = authenticated_request(:get, "#{odata_id_path}?$expand=*($levels=1)")
134
121
 
135
122
  if response.status == 200
136
123
  begin
@@ -138,42 +125,42 @@ module IDRAC
138
125
  volumes = data["Members"].map do |vol|
139
126
  drives = vol["Links"]["Drives"]
140
127
  volume_data = {
141
- name: vol["Name"],
142
- capacity_bytes: vol["CapacityBytes"],
143
- volume_type: vol["VolumeType"],
144
- drives: drives,
145
- write_cache_policy: vol.dig("Oem", "Dell", "DellVirtualDisk", "WriteCachePolicy"),
146
- read_cache_policy: vol.dig("Oem", "Dell", "DellVirtualDisk", "ReadCachePolicy"),
147
- stripe_size: vol.dig("Oem", "Dell", "DellVirtualDisk", "StripeSize"),
148
- raid_level: vol["RAIDType"],
149
- encrypted: vol["Encrypted"],
150
- lock_status: vol.dig("Oem", "Dell", "DellVirtualDisk", "LockStatus"),
151
- odata_id: vol["@odata.id"]
128
+ "name" => vol["Name"],
129
+ "capacity_bytes" => vol["CapacityBytes"],
130
+ "volume_type" => vol["VolumeType"],
131
+ "drives" => drives,
132
+ "write_cache_policy" => vol.dig("Oem", "Dell", "DellVirtualDisk", "WriteCachePolicy"),
133
+ "read_cache_policy" => vol.dig("Oem", "Dell", "DellVirtualDisk", "ReadCachePolicy"),
134
+ "stripe_size" => vol.dig("Oem", "Dell", "DellVirtualDisk", "StripeSize"),
135
+ "raid_level" => vol["RAIDType"],
136
+ "encrypted" => vol["Encrypted"],
137
+ "lock_status" => vol.dig("Oem", "Dell", "DellVirtualDisk", "LockStatus"),
138
+ "@odata.id" => vol["@odata.id"]
152
139
  }
153
140
 
154
141
  # Check FastPath settings
155
- volume_data[:fastpath] = fastpath_good?(vol)
142
+ volume_data["fastpath"] = fastpath_good?(volume_data)
156
143
 
157
144
  # Handle volume operations and status
158
145
  if vol["Operations"].any?
159
- volume_data[:health] = vol["Status"]["Health"] ? vol["Status"]["Health"] : "N/A"
160
- volume_data[:progress] = vol["Operations"].first["PercentageComplete"]
161
- volume_data[:message] = vol["Operations"].first["OperationName"]
162
- elsif vol["Status"]["Health"] == "OK"
163
- volume_data[:health] = "OK"
164
- volume_data[:progress] = nil
165
- volume_data[:message] = nil
146
+ volume_data["health"] = vol.dig("Status", "Health") || "N/A"
147
+ volume_data["progress"] = vol["Operations"].first["PercentageComplete"]
148
+ volume_data["message"] = vol["Operations"].first["OperationName"]
149
+ elsif vol.dig("Status", "Health") == "OK"
150
+ volume_data["health"] = "OK"
151
+ volume_data["progress"] = nil
152
+ volume_data["message"] = nil
166
153
  else
167
- volume_data[:health] = "?"
168
- volume_data[:progress] = nil
169
- volume_data[:message] = nil
154
+ volume_data["health"] = "?"
155
+ volume_data["progress"] = nil
156
+ volume_data["message"] = nil
170
157
  end
171
158
 
172
- # Create the RecursiveOpenStruct after all properties are set
173
- RecursiveOpenStruct.new(volume_data, recurse_over_arrays: true)
159
+ # Return the hash directly
160
+ volume_data
174
161
  end
175
162
 
176
- return volumes.sort_by { |d| d.name }
163
+ return volumes.sort_by { |d| d["name"] }
177
164
  rescue JSON::ParserError
178
165
  raise Error, "Failed to parse volumes response: #{response.body}"
179
166
  end
@@ -187,9 +174,9 @@ module IDRAC
187
174
  return "disabled" unless volume
188
175
 
189
176
  # Modern firmware check handled by caller
190
- if volume[:write_cache_policy] == "WriteThrough" &&
191
- volume[:read_cache_policy] == "NoReadAhead" &&
192
- volume[:stripe_size] == "64KB"
177
+ if volume["write_cache_policy"] == "WriteThrough" &&
178
+ volume["read_cache_policy"] == "NoReadAhead" &&
179
+ volume["stripe_size"] == "64KB"
193
180
  return "enabled"
194
181
  else
195
182
  return "disabled"
@@ -367,7 +354,7 @@ module IDRAC
367
354
 
368
355
  # Check if all physical disks are Self-Encrypting Drives
369
356
  def all_seds?(drives)
370
- drives.all? { |d| d.encryption_ability == "SelfEncryptingDrive" }
357
+ drives.all? { |d| d["encryption_ability"] == "SelfEncryptingDrive" }
371
358
  end
372
359
 
373
360
  # Check if the system is ready for SED operations
@@ -402,5 +389,15 @@ module IDRAC
402
389
  end
403
390
  end
404
391
  end
392
+
393
+ # Check if the controller is capable of encryption
394
+ def controller_encryption_capable?(controller)
395
+ controller.dig("encryption_capability") =~ /localkey/i
396
+ end
397
+
398
+ # Check if controller encryption is enabled
399
+ def controller_encryption_enabled?(controller)
400
+ controller.dig("encryption_mode") =~ /localkey/i
401
+ end
405
402
  end
406
- end
403
+ end
data/lib/idrac/system.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'json'
2
2
  require 'colorize'
3
- require 'recursive-open-struct'
4
3
 
5
4
  module IDRAC
6
5
  module System
@@ -11,28 +10,25 @@ module IDRAC
11
10
  if response.status == 200
12
11
  begin
13
12
  data = JSON.parse(response.body)
13
+
14
14
  memory = data["Members"].map do |m|
15
15
  dimm_name = m["Name"] # e.g. DIMM A1
16
- bank, index = /DIMM ([A-Z])(\d+)/.match(dimm_name).captures rescue [nil, nil]
17
-
18
- puts "DIMM: #{m["Model"]} #{m["Name"]} > #{m["CapacityMiB"]}MiB > #{m["Status"]["Health"]} > #{m["OperatingSpeedMhz"]}MHz > #{m["PartNumber"]} / #{m["SerialNumber"]}"
16
+ bank, index = /DIMM ([A-Z])(\d+)/.match(dimm_name).captures
19
17
 
20
- memory_data = {
21
- model: m["Model"],
22
- name: m["Name"],
23
- capacity_bytes: m["CapacityMiB"].to_i.megabyte,
24
- health: m["Status"]["Health"] ? m["Status"]["Health"] : "N/A",
25
- speed_mhz: m["OperatingSpeedMhz"],
26
- part_number: m["PartNumber"],
27
- serial: m["SerialNumber"],
28
- bank: bank,
29
- index: index.to_i
18
+ {
19
+ "model" => m["Model"],
20
+ "name" => m["Name"],
21
+ "capacity_bytes" => m["CapacityMiB"].to_i * 1024 * 1024,
22
+ "health" => m.dig("Status","Health") || "N/A",
23
+ "speed_mhz" => m["OperatingSpeedMhz"],
24
+ "part_number" => m["PartNumber"],
25
+ "serial" => m["SerialNumber"],
26
+ "bank" => bank,
27
+ "index" => index.to_i
30
28
  }
31
-
32
- RecursiveOpenStruct.new(memory_data, recurse_over_arrays: true)
33
29
  end
34
30
 
35
- return memory.sort_by { |m| [m.bank || "Z", m.index || 999] }
31
+ return memory.sort_by { |m| [m["bank"] || "Z", m["index"] || 999] }
36
32
  rescue JSON::ParserError
37
33
  raise Error, "Failed to parse memory response: #{response.body}"
38
34
  end
@@ -51,19 +47,17 @@ module IDRAC
51
47
  puts "Power Supplies".green
52
48
 
53
49
  psus = data["PowerSupplies"].map do |psu|
54
- puts "PSU: #{psu["Name"]} > #{psu["PowerInputWatts"]}W > #{psu["Status"]["Health"]}"
55
- psu_data = {
56
- name: psu["Name"],
57
- voltage: psu["LineInputVoltage"],
58
- voltage_human: psu["LineInputVoltageType"], # AC240V
59
- watts: psu["PowerInputWatts"],
60
- part: psu["PartNumber"],
61
- model: psu["Model"],
62
- serial: psu["SerialNumber"],
63
- status: psu["Status"]["Health"],
50
+ puts "PSU: #{psu["Name"]} > #{psu["PowerInputWatts"]}W > #{psu.dig("Status", "Health")}"
51
+ {
52
+ "name" => psu["Name"],
53
+ "voltage" => psu["LineInputVoltage"],
54
+ "voltage_human" => psu["LineInputVoltageType"], # AC240V
55
+ "watts" => psu["PowerInputWatts"],
56
+ "part" => psu["PartNumber"],
57
+ "model" => psu["Model"],
58
+ "serial" => psu["SerialNumber"],
59
+ "status" => psu.dig("Status", "Health")
64
60
  }
65
-
66
- RecursiveOpenStruct.new(psu_data, recurse_over_arrays: true)
67
61
  end
68
62
 
69
63
  return psus
@@ -88,15 +82,13 @@ module IDRAC
88
82
  data = JSON.parse(response.body)
89
83
 
90
84
  fans = data["Fans"].map do |fan|
91
- puts "Fan: #{fan["Name"]} > #{fan["Reading"]} > #{fan["Status"]["Health"]}"
92
- fan_data = {
93
- name: fan["Name"],
94
- rpm: fan["Reading"],
95
- serial: fan["SerialNumber"],
96
- status: fan["Status"]["Health"]
85
+ puts "Fan: #{fan["Name"]} > #{fan["Reading"]} > #{fan.dig("Status", "Health")}"
86
+ {
87
+ "name" => fan["Name"],
88
+ "rpm" => fan["Reading"],
89
+ "serial" => fan["SerialNumber"],
90
+ "status" => fan.dig("Status", "Health")
97
91
  }
98
-
99
- RecursiveOpenStruct.new(fan_data, recurse_over_arrays: true)
100
92
  end
101
93
 
102
94
  return fans
@@ -222,7 +214,7 @@ module IDRAC
222
214
 
223
215
  idrac = {
224
216
  "name" => data["Id"],
225
- "status" => data["Status"]["Health"] == 'OK' ? 'Up' : 'Down',
217
+ "status" => data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
226
218
  "mac" => data["MACAddress"],
227
219
  "mask" => data["IPv4Addresses"].first["SubnetMask"],
228
220
  "ipv4" => data["IPv4Addresses"].first["Address"],
@@ -293,15 +285,15 @@ module IDRAC
293
285
  def nics_to_pci(nics, pci_devices)
294
286
  # Filter for Mellanox network controllers
295
287
  mellanox_pci = pci_devices.select do |dev|
296
- dev[:device_class] =~ /NetworkController/ && dev[:manufacturer] =~ /Mellanox/
288
+ dev['device_class'] =~ /NetworkController/ && dev['manufacturer'] =~ /Mellanox/
297
289
  end
298
290
 
299
291
  # Create mapping of NIC names to PCI IDs
300
292
  mapping = {}
301
293
  mellanox_pci.each do |dev|
302
- if dev[:nic] && dev[:nic] =~ /.*\/([^\/\-]+-\d+)/
294
+ if dev['nic'] && dev['nic'] =~ /.*\/([^\/\-]+-\d+)/
303
295
  nic = $1 # e.g. NIC.Slot.1-1
304
- if dev[:id] =~ /^(\d+)-\d+-\d/
296
+ if dev['id'] =~ /^(\d+)-\d+-\d/
305
297
  pci_bus = $1 # e.g. 59
306
298
  mapping[nic] = pci_bus
307
299
  end
@@ -332,6 +324,22 @@ module IDRAC
332
324
  return nics_with_pci
333
325
  end
334
326
 
327
+ # Kind of like a NIC, but serves a different purpose.
328
+ def idrac_interface
329
+ response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces/iDRAC.Embedded.1%23NIC.1")
330
+ idrac_data = JSON.parse(response.body)
331
+ {
332
+ "name" => idrac_data["Id"],
333
+ "status" => idrac_data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
334
+ "mac" => idrac_data["MACAddress"],
335
+ "mask" => idrac_data["IPv4Addresses"].first["SubnetMask"],
336
+ "ipv4" => idrac_data["IPv4Addresses"].first["Address"],
337
+ "origin" => idrac_data["IPv4Addresses"].first["AddressOrigin"], # DHCP or Static
338
+ "port" => nil,
339
+ "speed_mbps" => idrac_data["SpeedMbps"],
340
+ "kind" => "ethernet"
341
+ }
342
+ end
335
343
  # Get system identification information
336
344
  def system_info
337
345
  response = authenticated_request(:get, "/redfish/v1")
@@ -342,42 +350,42 @@ module IDRAC
342
350
 
343
351
  # Initialize return hash with defaults
344
352
  info = {
345
- is_dell: false,
346
- is_ancient_dell: false,
347
- product: data["Product"] || "Unknown",
348
- service_tag: nil,
349
- model: nil,
350
- idrac_version: data["RedfishVersion"],
351
- firmware_version: nil
353
+ "is_dell" => false,
354
+ "is_ancient_dell" => false,
355
+ "product" => data["Product"] || "Unknown",
356
+ "service_tag" => nil,
357
+ "model" => nil,
358
+ "idrac_version" => data["RedfishVersion"],
359
+ "firmware_version" => nil
352
360
  }
353
361
 
354
362
  # Check if it's a Dell iDRAC
355
363
  if data["Product"] == "Integrated Dell Remote Access Controller"
356
- info[:is_dell] = true
364
+ info["is_dell"] = true
357
365
 
358
366
  # Get service tag from Dell OEM data
359
- info[:service_tag] = data.dig("Oem", "Dell", "ServiceTag")
367
+ info["service_tag"] = data.dig("Oem", "Dell", "ServiceTag")
360
368
 
361
369
  # Get firmware version - try both common locations
362
- info[:firmware_version] = data["FirmwareVersion"] || data.dig("Oem", "Dell", "FirmwareVersion")
370
+ info["firmware_version"] = data["FirmwareVersion"] || data.dig("Oem", "Dell", "FirmwareVersion")
363
371
 
364
372
  # Get additional system information
365
373
  system_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
366
374
  if system_response.status == 200
367
375
  system_data = JSON.parse(system_response.body)
368
- info[:model] = system_data["Model"]
376
+ info["model"] = system_data["Model"]
369
377
  end
370
378
 
371
- return RecursiveOpenStruct.new(info, recurse_over_arrays: true)
379
+ return info
372
380
  else
373
381
  # Try to handle ancient Dell models where Product is null or non-standard
374
382
  if data["Product"].nil? || data.dig("Oem", "Dell")
375
- info[:is_ancient_dell] = true
376
- return RecursiveOpenStruct.new(info, recurse_over_arrays: true)
383
+ info["is_ancient_dell"] = true
384
+ return info
377
385
  end
378
386
  end
379
387
 
380
- return RecursiveOpenStruct.new(info, recurse_over_arrays: true)
388
+ return info
381
389
  rescue JSON::ParserError
382
390
  raise Error, "Failed to parse system information: #{response.body}"
383
391
  end
@@ -395,14 +403,14 @@ module IDRAC
395
403
  data = JSON.parse(response.body)
396
404
 
397
405
  summary = {
398
- count: data.dig("ProcessorSummary", "Count"),
399
- model: data.dig("ProcessorSummary", "Model"),
400
- cores: data.dig("ProcessorSummary", "CoreCount"),
401
- threads: data.dig("ProcessorSummary", "LogicalProcessorCount"),
402
- status: data.dig("ProcessorSummary", "Status", "Health")
406
+ "count" => data.dig("ProcessorSummary", "Count"),
407
+ "model" => data.dig("ProcessorSummary", "Model"),
408
+ "cores" => data.dig("ProcessorSummary", "CoreCount"),
409
+ "threads" => data.dig("ProcessorSummary", "LogicalProcessorCount"),
410
+ "status" => data.dig("ProcessorSummary", "Status", "Health")
403
411
  }
404
412
 
405
- return RecursiveOpenStruct.new(summary, recurse_over_arrays: true)
413
+ return summary
406
414
  rescue JSON::ParserError
407
415
  raise Error, "Failed to parse processor information: #{response.body}"
408
416
  end
@@ -420,14 +428,14 @@ module IDRAC
420
428
  data = JSON.parse(response.body)
421
429
 
422
430
  health = {
423
- overall: data.dig("Status", "HealthRollup"),
424
- system: data.dig("Status", "Health"),
425
- processor: data.dig("ProcessorSummary", "Status", "Health"),
426
- memory: data.dig("MemorySummary", "Status", "Health"),
427
- storage: data.dig("Storage", "Status", "Health")
431
+ "overall" => data.dig("Status", "HealthRollup"),
432
+ "system" => data.dig("Status", "Health"),
433
+ "processor" => data.dig("ProcessorSummary", "Status", "Health"),
434
+ "memory" => data.dig("MemorySummary", "Status", "Health"),
435
+ "storage" => data.dig("Storage", "Status", "Health")
428
436
  }
429
437
 
430
- return RecursiveOpenStruct.new(health, recurse_over_arrays: true)
438
+ return health
431
439
  rescue JSON::ParserError
432
440
  raise Error, "Failed to parse system health information: #{response.body}"
433
441
  end
data/lib/idrac/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IDRAC
4
- VERSION = "0.5.10"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/idrac.rb CHANGED
@@ -7,7 +7,6 @@ require 'faraday/multipart'
7
7
  require 'base64'
8
8
  require 'uri'
9
9
  require 'colorize'
10
- require 'recursive-open-struct'
11
10
  require 'active_support'
12
11
  require 'active_support/core_ext'
13
12
  # If dev, required debug
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: idrac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Siegel
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2025-04-24 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: httparty
@@ -149,26 +150,6 @@ dependencies:
149
150
  - - ">="
150
151
  - !ruby/object:Gem::Version
151
152
  version: 1.1.0
152
- - !ruby/object:Gem::Dependency
153
- name: recursive-open-struct
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - "~>"
157
- - !ruby/object:Gem::Version
158
- version: '1.1'
159
- - - ">="
160
- - !ruby/object:Gem::Version
161
- version: 1.1.0
162
- type: :runtime
163
- prerelease: false
164
- version_requirements: !ruby/object:Gem::Requirement
165
- requirements:
166
- - - "~>"
167
- - !ruby/object:Gem::Version
168
- version: '1.1'
169
- - - ">="
170
- - !ruby/object:Gem::Version
171
- version: 1.1.0
172
153
  - !ruby/object:Gem::Dependency
173
154
  name: activesupport
174
155
  requirement: !ruby/object:Gem::Requirement
@@ -297,6 +278,7 @@ licenses:
297
278
  - MIT
298
279
  metadata:
299
280
  homepage_uri: http://github.com
281
+ post_install_message:
300
282
  rdoc_options: []
301
283
  require_paths:
302
284
  - lib
@@ -311,7 +293,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
311
293
  - !ruby/object:Gem::Version
312
294
  version: '0'
313
295
  requirements: []
314
- rubygems_version: 3.6.7
296
+ rubygems_version: 3.5.16
297
+ signing_key:
315
298
  specification_version: 4
316
299
  summary: API Client for Dell iDRAC
317
300
  test_files: []