idrac 0.7.1 → 0.7.2

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/system.rb CHANGED
@@ -121,78 +121,110 @@ module IDRAC
121
121
  begin
122
122
  adapters_data = JSON.parse(response.body)
123
123
 
124
- # Determine iDRAC version for different port paths
125
- idrac_version_response = authenticated_request(:get, "/redfish/v1")
126
- idrac_version_data = JSON.parse(idrac_version_response.body)
127
- server = idrac_version_data["RedfishVersion"] || idrac_version_response.headers["server"]
124
+ # Try to get network configuration from SCP for IP addresses
125
+ nic_ip_map = {}
128
126
 
129
- is_idrac9 = case server.to_s.downcase
130
- when /idrac\/9/
131
- true
132
- when /idrac\/8/
133
- false
134
- when /appweb\/4.5/
135
- false
136
- else
137
- # Default to newer format for unknown versions
138
- true
139
- end
127
+ # Try to get IP data from SCP
128
+ begin
129
+ scp_data = get_system_configuration_profile(target: "NIC")
130
+ if scp_data && scp_data["SystemConfiguration"] && scp_data["SystemConfiguration"]["Components"]
131
+ scp_data["SystemConfiguration"]["Components"].each do |component|
132
+ next unless component["FQDD"] =~ /NIC\.|BCM57/ # Match NIC components
133
+
134
+ # Extract IP configuration if available
135
+ ip_attrs = component["Attributes"]&.select { |attr| attr["Name"] =~ /IPAddress|IPv4Address|IPV4Address/ }
136
+ mac_attrs = component["Attributes"]&.select { |attr| attr["Name"] =~ /MACAddress/ }
137
+
138
+ if ip_attrs&.any? && mac_attrs&.any?
139
+ mac = mac_attrs.first["Value"]
140
+ ip = ip_attrs.first["Value"]
141
+ nic_ip_map[mac&.upcase] = ip unless ip.nil? || ip.empty? || ip == "0.0.0.0"
142
+ end
143
+ end
144
+ end
145
+ rescue => e
146
+ # Ignore errors, just continue
147
+ end
140
148
 
141
- port_part = is_idrac9 ? 'Ports' : 'NetworkPorts'
149
+ # Try to get static IP configuration from iDRAC
150
+ begin
151
+ idrac_net = idrac_network
152
+ if idrac_net && idrac_net["mac"] && idrac_net["ipv4"]
153
+ nic_ip_map[idrac_net["mac"].upcase] = idrac_net["ipv4"]
154
+ end
155
+ rescue => e
156
+ # Ignore errors, just continue
157
+ end
158
+
159
+ nics = []
142
160
 
143
- nics = adapters_data["Members"].map do |adapter|
144
- path = "#{adapter["@odata.id"].split("v1/").last}/#{port_part}?$expand=*($levels=1)"
145
- ports_response = authenticated_request(:get, "/redfish/v1/#{path}")
161
+ # Process each network adapter
162
+ adapters_data["Members"].each do |adapter|
163
+ # Get basic adapter info
164
+ adapter_info = {
165
+ "name" => adapter["Id"],
166
+ "manufacturer" => adapter["Manufacturer"],
167
+ "model" => adapter["Model"],
168
+ "part_number" => adapter["PartNumber"],
169
+ "serial" => adapter["SerialNumber"],
170
+ "ports" => []
171
+ }
146
172
 
147
- if ports_response.status == 200
148
- ports_data = JSON.parse(ports_response.body)
173
+ # Try each port type in order of preference
174
+ ["NetworkPorts", "Ports"].each do |port_type|
175
+ ports_path = "#{adapter["@odata.id"].split("v1/").last}/#{port_type}?$expand=*($levels=1)"
149
176
 
150
- ports = ports_data["Members"].map do |nic|
151
- if is_idrac9
152
- link_speed_mbps = nic['CurrentSpeedGbps'].to_i * 1000
153
- mac_addr = nic['Ethernet']['AssociatedMACAddresses'].first
154
- port_num = nic['PortId']
155
- network_technology = nic['LinkNetworkTechnology']
156
- link_status = nic['LinkStatus'] =~ /up/i ? "Up" : "Down"
157
- else # iDRAC 8
158
- link_speed_mbps = nic["SupportedLinkCapabilities"].first["LinkSpeedMbps"]
159
- mac_addr = nic["AssociatedNetworkAddresses"].first
160
- port_num = nic["PhysicalPortNumber"]
161
- network_technology = nic["SupportedLinkCapabilities"].first["LinkNetworkTechnology"]
162
- link_status = nic['LinkStatus']
163
- end
164
-
165
- puts "NIC: #{nic["Id"]} > #{mac_addr} > #{link_status} > #{port_num} > #{link_speed_mbps}Mbps"
177
+ begin
178
+ ports_response = authenticated_request(:get, "/redfish/v1/#{ports_path}")
166
179
 
167
- {
168
- "name" => nic["Id"],
169
- "status" => link_status,
170
- "mac" => mac_addr,
171
- "port" => port_num,
172
- "speed_mbps" => link_speed_mbps,
173
- "kind" => network_technology&.downcase
174
- }
180
+ if ports_response.status == 200
181
+ ports_data = JSON.parse(ports_response.body)
182
+
183
+ if ports_data["Members"] && ports_data["Members"].any?
184
+ # Process each port
185
+ adapter_info["ports"] = ports_data["Members"].map do |port|
186
+ # Extract port info based on port type
187
+ if port_type == "NetworkPorts"
188
+ # NetworkPorts style (usually iDRAC 8)
189
+ mac_addr = port["AssociatedNetworkAddresses"]&.first
190
+ link_speed_mbps = port.dig("SupportedLinkCapabilities", 0, "LinkSpeedMbps") || 0
191
+ port_num = port["PhysicalPortNumber"]
192
+ link_status = port["LinkStatus"]
193
+ else
194
+ # Ports style (usually iDRAC 9)
195
+ mac_addr = port.dig("Ethernet", "AssociatedMACAddresses", 0)
196
+ link_speed_mbps = port["CurrentSpeedGbps"] ? (port["CurrentSpeedGbps"].to_i * 1000) : 0
197
+ port_num = port["PortId"]
198
+ link_status = port["LinkStatus"] =~ /up/i ? "Up" : "Down"
199
+ end
200
+
201
+ # Get IP address from our mapping
202
+ ip_address = nic_ip_map[mac_addr&.upcase]
203
+
204
+ puts "NIC: #{port["Id"]} > #{mac_addr} > #{link_status} > #{port_num} > #{link_speed_mbps}Mbps > #{ip_address || 'No IP'}"
205
+
206
+ {
207
+ "name" => port["Id"],
208
+ "status" => link_status,
209
+ "mac" => mac_addr,
210
+ "ip_address" => ip_address,
211
+ "port" => port_num,
212
+ "speed_mbps" => link_speed_mbps,
213
+ "kind" => port_type == "NetworkPorts" ? "ethernet" : "port"
214
+ }
215
+ end
216
+
217
+ # If we found ports, no need to try the other endpoint
218
+ break
219
+ end
220
+ end
221
+ rescue => e
222
+ # Ignore errors and try the next port type
175
223
  end
176
-
177
- {
178
- "name" => adapter["Id"],
179
- "manufacturer" => adapter["Manufacturer"],
180
- "model" => adapter["Model"],
181
- "part_number" => adapter["PartNumber"],
182
- "serial" => adapter["SerialNumber"],
183
- "ports" => ports
184
- }
185
- else
186
- # Return adapter info without ports if we can't get port details
187
- {
188
- "name" => adapter["Id"],
189
- "manufacturer" => adapter["Manufacturer"],
190
- "model" => adapter["Model"],
191
- "part_number" => adapter["PartNumber"],
192
- "serial" => adapter["SerialNumber"],
193
- "ports" => []
194
- }
195
224
  end
225
+
226
+ # Add adapter to our list
227
+ nics << adapter_info
196
228
  end
197
229
 
198
230
  return nics
@@ -206,30 +238,72 @@ module IDRAC
206
238
 
207
239
  # Get iDRAC network information
208
240
  def idrac_network
209
- response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces/iDRAC.Embedded.1%23NIC.1")
241
+ # First try to get the EthernetInterfaces collection to get the correct path
242
+ collection_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces")
210
243
 
211
- if response.status == 200
244
+ if collection_response.status == 200
212
245
  begin
213
- data = JSON.parse(response.body)
214
-
215
- idrac = {
216
- "name" => data["Id"],
217
- "status" => data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
218
- "mac" => data["MACAddress"],
219
- "mask" => data["IPv4Addresses"].first["SubnetMask"],
220
- "ipv4" => data["IPv4Addresses"].first["Address"],
221
- "origin" => data["IPv4Addresses"].first["AddressOrigin"], # DHCP or Static
222
- "port" => nil,
223
- "speed_mbps" => data["SpeedMbps"],
224
- "kind" => "ethernet"
225
- }
246
+ collection_data = JSON.parse(collection_response.body)
226
247
 
227
- return idrac
248
+ if collection_data["Members"] && collection_data["Members"].any?
249
+ # Use the first interface found
250
+ interface_path = collection_data["Members"][0]["@odata.id"]
251
+ debug "Using interface path: #{interface_path}", 2
252
+
253
+ response = authenticated_request(:get, interface_path)
254
+
255
+ if response.status == 200
256
+ data = JSON.parse(response.body)
257
+
258
+ idrac = {
259
+ "name" => data["Id"],
260
+ "status" => data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
261
+ "mac" => data["MACAddress"],
262
+ "mask" => data["IPv4Addresses"].first["SubnetMask"],
263
+ "ipv4" => data["IPv4Addresses"].first["Address"],
264
+ "origin" => data["IPv4Addresses"].first["AddressOrigin"], # DHCP or Static
265
+ "port" => nil,
266
+ "speed_mbps" => data["SpeedMbps"],
267
+ "kind" => "ethernet"
268
+ }
269
+
270
+ return idrac
271
+ else
272
+ raise Error, "Failed to get iDRAC network. Status code: #{response.status}"
273
+ end
274
+ else
275
+ raise Error, "No Ethernet interfaces found"
276
+ end
228
277
  rescue JSON::ParserError
229
- raise Error, "Failed to parse iDRAC network response: #{response.body}"
278
+ raise Error, "Failed to parse iDRAC network response: #{collection_response.body}"
230
279
  end
231
280
  else
232
- raise Error, "Failed to get iDRAC network. Status code: #{response.status}"
281
+ # Fallback to the old hard-coded path for backwards compatibility
282
+ response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces/iDRAC.Embedded.1%23NIC.1")
283
+
284
+ if response.status == 200
285
+ begin
286
+ data = JSON.parse(response.body)
287
+
288
+ idrac = {
289
+ "name" => data["Id"],
290
+ "status" => data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
291
+ "mac" => data["MACAddress"],
292
+ "mask" => data["IPv4Addresses"].first["SubnetMask"],
293
+ "ipv4" => data["IPv4Addresses"].first["Address"],
294
+ "origin" => data["IPv4Addresses"].first["AddressOrigin"], # DHCP or Static
295
+ "port" => nil,
296
+ "speed_mbps" => data["SpeedMbps"],
297
+ "kind" => "ethernet"
298
+ }
299
+
300
+ return idrac
301
+ rescue JSON::ParserError
302
+ raise Error, "Failed to parse iDRAC network response: #{response.body}"
303
+ end
304
+ else
305
+ raise Error, "Failed to get iDRAC network. Status code: #{response.status}"
306
+ end
233
307
  end
234
308
  end
235
309
 
@@ -437,6 +511,38 @@ module IDRAC
437
511
 
438
512
  # Kind of like a NIC, but serves a different purpose.
439
513
  def idrac_interface
514
+ # First try to get the EthernetInterfaces collection to get the correct path
515
+ collection_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces")
516
+
517
+ if collection_response.status == 200
518
+ collection_data = JSON.parse(collection_response.body)
519
+
520
+ if collection_data["Members"] && collection_data["Members"].any?
521
+ # Use the first interface found
522
+ interface_path = collection_data["Members"][0]["@odata.id"]
523
+ debug "Using interface path: #{interface_path}", 2
524
+
525
+ response = authenticated_request(:get, interface_path)
526
+
527
+ if response.status == 200
528
+ idrac_data = JSON.parse(response.body)
529
+
530
+ return {
531
+ "name" => idrac_data["Id"],
532
+ "status" => idrac_data.dig("Status", "Health") == 'OK' ? 'Up' : 'Down',
533
+ "mac" => idrac_data["MACAddress"],
534
+ "mask" => idrac_data["IPv4Addresses"].first["SubnetMask"],
535
+ "ipv4" => idrac_data["IPv4Addresses"].first["Address"],
536
+ "origin" => idrac_data["IPv4Addresses"].first["AddressOrigin"], # DHCP or Static
537
+ "port" => nil,
538
+ "speed_mbps" => idrac_data["SpeedMbps"],
539
+ "kind" => "ethernet"
540
+ }
541
+ end
542
+ end
543
+ end
544
+
545
+ # Fallback to the old hard-coded path
440
546
  response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces/iDRAC.Embedded.1%23NIC.1")
441
547
  idrac_data = JSON.parse(response.body)
442
548
  {
@@ -451,6 +557,7 @@ module IDRAC
451
557
  "kind" => "ethernet"
452
558
  }
453
559
  end
560
+
454
561
  # Get system identification information
455
562
  def system_info
456
563
  response = authenticated_request(:get, "/redfish/v1")
@@ -606,7 +713,7 @@ module IDRAC
606
713
 
607
714
  # Get total memory in human-readable format
608
715
  def total_memory_human(memory_data)
609
- total_memory = memory_data.sum { |m| m.capacity_bytes }
716
+ total_memory = memory_data.sum { |m| m["capacity_bytes"] }
610
717
  "%0.2f GB" % (total_memory.to_f / 1.gigabyte)
611
718
  end
612
719
 
@@ -631,63 +738,53 @@ module IDRAC
631
738
  idrac_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1")
632
739
  idrac_info = JSON.parse(idrac_response.body)
633
740
 
634
- # Get network information
635
- network_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces/NIC.1")
636
- network_info = JSON.parse(network_response.body)
637
-
638
- # Initialize license_type to Unknown
639
- license_type = "Unknown"
640
- license_description = nil
741
+ # Initialize network info values to Unknown
742
+ ip_address = "Unknown"
743
+ mac_address = "Unknown"
641
744
 
642
- # Try to get license information using DMTF standard method
745
+ # Try to get network information
643
746
  begin
644
- license_response = authenticated_request(:get, "/redfish/v1/LicenseService/Licenses")
645
- license_info = JSON.parse(license_response.body)
747
+ # First, get the EthernetInterfaces collection to find available interfaces
748
+ eth_collection_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/EthernetInterfaces")
646
749
 
647
- # Extract license type if licenses are found
648
- if license_info["Members"] && !license_info["Members"].empty?
649
- license_entry_response = authenticated_request(:get, license_info["Members"][0]["@odata.id"])
650
- license_entry = JSON.parse(license_entry_response.body)
750
+ if eth_collection_response.status == 200
751
+ eth_collection = JSON.parse(eth_collection_response.body)
651
752
 
652
- # Get license type from EntitlementId or LicenseType
653
- if license_entry["EntitlementId"] && license_entry["EntitlementId"].include?("Enterprise")
654
- license_type = "Enterprise"
655
- elsif license_entry["LicenseType"]
656
- license_type = license_entry["LicenseType"]
657
- end
658
-
659
- # Get license description if available
660
- license_description = license_entry["Description"] if license_entry["Description"]
661
- end
662
- rescue => e
663
- # If DMTF method fails, try Dell OEM method
664
- begin
665
- dell_license_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLicenses")
666
- dell_license_info = JSON.parse(dell_license_response.body)
667
-
668
- # Extract license type if licenses are found
669
- if dell_license_info["Members"] && !dell_license_info["Members"].empty?
670
- dell_license_entry_response = authenticated_request(:get, dell_license_info["Members"][0]["@odata.id"])
671
- dell_license_entry = JSON.parse(dell_license_entry_response.body)
753
+ if eth_collection["Members"] && eth_collection["Members"].any?
754
+ # Use the first interface found
755
+ first_eth = eth_collection["Members"][0]["@odata.id"]
756
+ debug "Found network interface: #{first_eth}", 2
672
757
 
673
- # Get license type from LicenseType or Description
674
- if dell_license_entry["LicenseType"]
675
- license_type = dell_license_entry["LicenseType"]
676
- elsif dell_license_entry["Description"] && dell_license_entry["Description"].include?("Enterprise")
677
- license_type = "Enterprise"
758
+ first_eth_response = authenticated_request(:get, first_eth)
759
+ if first_eth_response.status == 200
760
+ first_eth_info = JSON.parse(first_eth_response.body)
761
+ ip_address = first_eth_info.dig("IPv4Addresses", 0, "Address") || "Unknown"
762
+ mac_address = first_eth_info.dig("MACAddress") || "Unknown"
763
+ debug "Network info - IP: #{ip_address}, MAC: #{mac_address}", 2
678
764
  end
679
-
680
- # Get license description if available
681
- license_description = dell_license_entry["Description"] if dell_license_entry["Description"]
682
765
  end
683
- rescue => e2
684
- # License information not available
685
766
  end
767
+ rescue => e
768
+ debug "Failed to get network info: #{e.message}", 1, :yellow
769
+ # Failed to get network info, leave as Unknown
770
+ end
771
+
772
+ # Initialize license_type to Unknown
773
+ license_type = "Unknown"
774
+ license_description = nil
775
+
776
+ # Try to get license information (new approach that works with iDRAC 8 too)
777
+ license_info = license_info() rescue nil
778
+ license_version = license_version() rescue nil
779
+
780
+ if license_info
781
+ license_type = license_info["LicenseType"] || "Unknown"
782
+ license_description = license_info["Description"]
686
783
  end
687
784
 
688
785
  # Format the license display string
689
786
  license_display = license_type
690
- if license_description
787
+ if license_description && license_type != "Unknown"
691
788
  license_display = "#{license_type} (#{license_description})"
692
789
  end
693
790
 
@@ -701,8 +798,8 @@ module IDRAC
701
798
  service_tag: system_info["SKU"],
702
799
  bios_version: system_info.dig("BiosVersion"),
703
800
  idrac_firmware: idrac_info.dig("FirmwareVersion"),
704
- ip_address: network_info.dig("IPv4Addresses", 0, "Address"),
705
- mac_address: network_info.dig("MACAddress"),
801
+ ip_address: ip_address,
802
+ mac_address: mac_address,
706
803
  license: license_display
707
804
  }
708
805
  end