module IDRAC::License

def create_fallback_license_info(use_basic: false)

Returns:
  • (Hash) - A basic license info object
def create_fallback_license_info(use_basic: false)
  # Get the iDRAC version number from server headers
  version = nil
  response = authenticated_request(:get, "/redfish/v1")
  if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
    version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
  end
  
  # Try to determine if it's Enterprise or Express based on available features
  license_type = use_basic ? "Basic" : determine_license_type
  
  license_info = {
    "Id" => "iDRAC-License",
    "Description" => version ? "iDRAC#{version} #{license_type} License" : "iDRAC License",
    "Name" => "iDRAC License",
    "LicenseType" => license_type,
    "Status" => { "Health" => "OK" },
    "Removable" => false
  }
  
  debug "Created fallback license info: #{license_info}", 2
  license_info
end

def determine_license_type

Returns:
  • (String) - The license type (Enterprise, Express, or Basic)
def determine_license_type
  # We can try to check for features only available in Enterprise
  begin
    # For example, virtual media is typically an Enterprise feature
    virtual_media_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/VirtualMedia")
    # If we successfully get virtual media, it's likely Enterprise
    if virtual_media_response.status == 200
      return "Enterprise" 
    end
  rescue
    # If the request fails, don't fail the whole method
  end
  
  # Default to basic license if we can't determine
  return "Express"
end

def get_license_type_from_description(description)

Helper method to extract license type from description
def get_license_type_from_description(description)
  return "Unknown" unless description
  
  if description.include?("Enterprise")
    return "Enterprise"
  elsif description.include?("Express")
    return "Express"
  elsif description.include?("Datacenter")
    return "Datacenter"
  else
    return "Basic"
  end
end

def get_service_tag

Get service tag from system info
def get_service_tag
  response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
  if response.status == 200
    data = JSON.parse(response.body)
    return data["SKU"] if data["SKU"] # Service tag is usually in SKU field
  end
  
  # Try alternate location
  response = authenticated_request(:get, "/redfish/v1")
  if response.status == 200
    data = JSON.parse(response.body)
    if data["Oem"] && data["Oem"]["Dell"] && data["Oem"]["Dell"]["ServiceTag"]
      return data["Oem"]["Dell"]["ServiceTag"]
    end
  end
  
  nil
end

def handle_dell_attributes_response(data)

Handle response from Dell Manager attributes
def handle_dell_attributes_response(data)
  # Look for license information in attributes
  if data["Attributes"] && (
      data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] ||
      data["Attributes"]["System.ServerOS.1.OSName"] ||
      data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
    )
    
    license_info = data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] || 
                   data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
    
    if license_info
      license_type = license_info.include?("Enterprise") ? "Enterprise" : 
                     license_info.include?("Express") ? "Express" : "Basic"
      
      return {
        "Id" => "iDRAC-License",
        "Description" => "iDRAC8 #{license_type} License",
        "Name" => "iDRAC License",
        "LicenseType" => license_type,
        "Status" => { "Health" => "OK" },
        "Removable" => false
      }
    end
  end
  
  # If no license attributes found, fall back to feature detection
  license_type = determine_license_type()
  return {
    "Id" => "iDRAC-License",
    "Description" => "iDRAC8 #{license_type} License",
    "Name" => "iDRAC License",
    "LicenseType" => license_type,
    "Status" => { "Health" => "OK" },
    "Removable" => false
  }
end

def handle_dell_license_service_response(data)

Handle response from Dell License Management Service
def handle_dell_license_service_response(data)
  # Check if there are any license entries
  if !data["Members"] || data["Members"].empty?
    debug "No licenses found in Dell OEM path", 1, :yellow
    return create_fallback_license_info(use_basic: true)
  end
  
  # Get the first license in the list
  license_uri = data["Members"][0]["@odata.id"]
  debug "Using Dell OEM license URI: #{license_uri}", 2
  
  # Get detailed license information
  license_response = authenticated_request(:get, license_uri)
  if license_response.status != 200
    debug "Failed to retrieve Dell OEM license details: #{license_response.status}", 1, :red
    return create_fallback_license_info(use_basic: true)
  end
  
  dell_license = JSON.parse(license_response.body)
  debug "Dell OEM license details: #{dell_license}", 2
  
  # Convert Dell OEM license format to standard format
  license_info = {
    "Id" => dell_license["EntitlementID"] || "iDRAC-License",
    "Description" => dell_license["LicenseDescription"] || "iDRAC License",
    "Name" => dell_license["LicenseDescription"] || "iDRAC License",
    "LicenseType" => dell_license["LicenseType"] || get_license_type_from_description(dell_license["LicenseDescription"]),
    "Status" => { "Health" => "OK" },
    "Removable" => true
  }
  
  return license_info
end

def handle_dell_manager_response(data)

Handle response from Dell Manager entity
def handle_dell_manager_response(data)
  # Look for license information in Oem data
  if data["Oem"] && data["Oem"]["Dell"]
    dell_data = data["Oem"]["Dell"]
    
    if dell_data["DellLicenseManagementService"]
      # Found license service reference, but need to query it directly
      service_uri = dell_data["DellLicenseManagementService"]["@odata.id"]
      debug "Found license service URI: #{service_uri}", 2
      
      service_response = authenticated_request(:get, service_uri)
      if service_response.status == 200
        return handle_dell_license_service_response(JSON.parse(service_response.body))
      end
    end
    
    # Check if license info is embedded directly
    if dell_data["LicenseType"] || dell_data["License"]
      license_type = dell_data["LicenseType"] || 
                    (dell_data["License"] && dell_data["License"].include?("Enterprise") ? "Enterprise" : 
                     dell_data["License"].include?("Express") ? "Express" : "Basic")
      
      return {
        "Id" => "iDRAC-License",
        "Description" => "iDRAC8 #{license_type} License",
        "Name" => "iDRAC License",
        "LicenseType" => license_type,
        "Status" => { "Health" => "OK" },
        "Removable" => false
      }
    end
  end
  
  # Check for license type in model name or description
  if data["Model"] && data["Model"].include?("Enterprise")
    return {
      "Id" => "iDRAC-License",
      "Description" => "iDRAC8 Enterprise License",
      "Name" => "iDRAC License",
      "LicenseType" => "Enterprise",
      "Status" => { "Health" => "OK" },
      "Removable" => false
    }
  end
  
  # If no license info found in manager data, fall back to feature detection
  license_type = determine_license_type()
  return {
    "Id" => "iDRAC-License",
    "Description" => "iDRAC8 #{license_type} License",
    "Name" => "iDRAC License",
    "LicenseType" => license_type,
    "Status" => { "Health" => "OK" },
    "Removable" => false
  }
end

def license_info

Returns:
  • (Hash) - License details
def license_info
  # Try the standard license endpoint first (works in iDRAC 9+)
  response = authenticated_request(:get, "/redfish/v1/LicenseService/Licenses")
  
  if response.status == 200
    license_data = JSON.parse(response.body)
    debug "License collection: #{license_data}", 2
    # Check if there are any license entries
    if !license_data["Members"] || license_data["Members"].empty?
      debug "No licenses found", 1, :yellow
      return try_dell_oem_license_path()
    end
    # Get the first license in the list
    license_uri = license_data["Members"][0]["@odata.id"]
    debug "Using license URI: #{license_uri}", 2
    # Get detailed license information
    license_response = authenticated_request(:get, license_uri)
    if license_response.status != 200
      debug "Failed to retrieve license details: #{license_response.status}", 1, :red
      return try_dell_oem_license_path()
    end
    license_details = JSON.parse(license_response.body)
    debug "License details: #{license_details}", 2
    return license_details
  else
    # The endpoint is not available (probably iDRAC 8)
    debug "Standard license endpoint failed: #{response.status}, trying Dell OEM path", 1, :yellow
    return try_dell_oem_license_path()
  end
end

def license_version

Returns:
  • (Integer, nil) - The license version (e.g. 9) or nil if not found
def license_version
  # First try to get from license info
  license = license_info
  if license
    # Check the Description field, which often contains the version
    # Example: "iDRAC9 Enterprise License"
    if license["Description"]&.match(/iDRAC(\d+)/i)
      version = license["Description"].match(/iDRAC(\d+)/i)[1].to_i
      debug "Found license version from Description: #{version}", 1
      return version
    end
    # Try alternative fields if Description didn't work
    if license["Name"]&.match(/iDRAC(\d+)/i)
      version = license["Name"].match(/iDRAC(\d+)/i)[1].to_i
      debug "Found license version from Name: #{version}", 1
      return version
    end
    
    # For Dell OEM license response format
    if license["LicenseDescription"]&.match(/iDRAC(\d+)/i)
      version = license["LicenseDescription"].match(/iDRAC(\d+)/i)[1].to_i
      debug "Found license version from LicenseDescription: #{version}", 1
      return version
    end
  end
  
  # If license info failed or didn't have version info, try to get from server header
  # Make a simple request to check the server header (often contains iDRAC version)
  response = authenticated_request(:get, "/redfish/v1")
  if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
    version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
    debug "Found license version from server header: #{version}", 1
    return version
  end
  
  debug "Could not determine license version from license info or server header", 1, :yellow
  nil
end

def try_dell_oem_license_path

Returns:
  • (Hash, nil) - License info or nil if not found
def try_dell_oem_license_path
  # Try several potential Dell license paths (order matters - most likely first)
  dell_license_paths = [
    "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLicenseManagementService/Licenses",
    "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes", # iDRAC attributes might contain license info
    "/redfish/v1/Managers/iDRAC.Embedded.1"             # Manager entity might have license info embedded
  ]
  
  dell_license_paths.each do |path|
    response = authenticated_request(:get, path)
    
    if response.status == 200
      debug "Found valid Dell license path: #{path}", 2
      data = JSON.parse(response.body)
      
      # Check for license info in this response based on the path
      if path.include?("DellLicenseManagementService")
        return handle_dell_license_service_response(data)
      elsif path.include?("Attributes")
        return handle_dell_attributes_response(data)
      elsif path.include?("iDRAC.Embedded.1") && !path.include?("Attributes")
        return handle_dell_manager_response(data)
      end
    else
      debug "Dell path #{path} response status: #{response.status}", 3
    end
  end
  
  # If we couldn't find any API path that works, try the service tag detection method 
  service_tag = get_service_tag
  if service_tag
    # Service tag is often used to indicate Enterprise licenses on Dell systems
    license_type = determine_license_type()
    return {
      "Id" => "iDRAC-License",
      "Description" => "iDRAC8 #{license_type} License",
      "Name" => "iDRAC License",
      "LicenseType" => license_type,
      "Status" => { "Health" => "OK" },
      "Removable" => false,
      "EntitlementID" => service_tag # Dell often uses service tag as entitlement ID
    }
  end
  
  # Fall back to feature detection if all else fails
  debug "All Dell OEM license paths failed, using fallback detection", 1, :yellow
  return create_fallback_license_info()
end