class ComputeUnit::Device
Constants
- PROC_PATH
- SYSFS_DEVICES_PATH
We can supply a mock sysfs path in order to test with containers and other scenarios
Attributes
Public Class Methods
@return [Device] - creates a device from the given path @param device_path
[String] - the sysfs path to of the device
# File lib/compute_unit/device.rb, line 183 def self.create_from_path(device_path) opts = { device_class_id: device_class(device_path), device_id: device(device_path), device_vendor_id: device_vendor(device_path), subsystem_vendor_id: subsystem_vendor(device_path), subsystem_device_id: subsystem_device(device_path) } new(device_path, opts) end
@return [String] - the device number ie. 1002
# File lib/compute_unit/device.rb, line 246 def self.device(device_path) read_kernel_setting(device_path, 'device', '').slice(2, 6) end
@return [String] - the class number ie. 040300 @note helps determine what kind of device this is ie. graphics, storage, …
# File lib/compute_unit/device.rb, line 257 def self.device_class(device_path) read_kernel_setting(device_path, 'class', '').slice(2, 8) end
@param [String] - the device id such as '686f' queries the pci_database
that is shipped with this gem and looks up the device @return [String] - the device name
# File lib/compute_unit/device.rb, line 382 def self.device_lookup(device_id) # "\t687f Vega 10 XT [Radeon RX Vega 64]\n" re = Regexp.new(/\A\t#{device_id}/) d = pci_database.find do |line| re.match(line) rescue ArgumentError next end return d unless d name = d[/\t\w+\s+(.*)\n/, 1] name[/.*\[(.*)\]/, 1] || name if name end
@return [String] - the vendor number ie. 1002
# File lib/compute_unit/device.rb, line 251 def self.device_vendor(device_path) read_kernel_setting(device_path, 'vendor', '').slice(2, 6) end
@return [Array] - an array of pci bus device locations (every device on the pci bus) @param [String] - the device class for filtering out devices @note there is not a filter applied
# File lib/compute_unit/device.rb, line 197 def self.find_all(device_class_id = nil) Dir.glob(File.join(ComputeUnit::Device::SYSFS_DEVICES_PATH, '*')).map do |device_path| next create_from_path(device_path) unless device_class_id next create_from_path(device_path) if device_class(device_path) == device_class_id end.compact end
# File lib/compute_unit/device.rb, line 396 def self.logger ComputeUnit::Logger.logger end
# File lib/compute_unit/device.rb, line 315 def self.manual_device_database @manual_device_database ||= { '687f_0b36_1002' => 'Radeon RX Vega 64' } end
# File lib/compute_unit/device.rb, line 321 def self.manual_device_lookup(device_id, subsystem_device_id, vendor_id) key = "#{device_id}_#{subsystem_device_id}_#{vendor_id}" manual_device_database.fetch(key, nil) end
@return [String] - return the string of the device vendor @param id [String] - the vendor id
# File lib/compute_unit/device.rb, line 360 def self.manual_vendor_lookup(id) manual_vendors[id] end
@return [Hash] - a hash of vendors names and ids
# File lib/compute_unit/device.rb, line 354 def self.manual_vendors @manual_vendors ||= { '196e' => 'PNY' } end
@return [Hash] - Name translation map sometimes we just wamnt a shorter name to see
# File lib/compute_unit/device.rb, line 211 def self.name_map @name_map ||= { 'ASUSTeK COMPUTER INC.' => 'Asus', 'ASUSTeK Computer Inc.' => 'Asus', 'Advanced Micro Devices, Inc. [AMD/ATI]' => 'AMD', 'XFX Pine Group Inc.' => 'XFX', 'NVIDIA Corporation' => 'Nvidia', 'Gigabyte Technology Co., Ltd' => 'Gigabyte', 'Sapphire Technology Limited' => 'Sapphire', 'eVga.com. Corp.' => 'Evga', 'Micro-Star International Co., Ltd. [MSI]' => 'MSI', 'Micro-Star International Co., Ltd.' => 'MSI', 'Intel Corporation' => 'Intel', 'ASMedia Technology Inc.' => 'AsMedia Tech', 'Advanced Micro Devices, Inc. [AMD]' => 'AMD', 'Tul Corporation / PowerColor' => 'PowerColor', 'PC Partner Limited / Sapphire Technology' => 'Sapphire', 'Realtek Semiconductor Co., Ltd.' => 'Realtek', 'Samsung Electronics Co Ltd' => 'Samsung', 'ZOTAC International (MCO) Ltd.' => 'Zotac' } end
@return [String] - translation of vendor names to how we known them
# File lib/compute_unit/device.rb, line 205 def self.name_translation(name) name_map[name] || name end
# File lib/compute_unit/device.rb, line 26 def initialize(device_path, opts = {}) @device_path = device_path @device_class_id = opts[:device_class_id] @device_id = opts[:device_id] @device_vendor_id = opts[:device_vendor_id] @subsystem_vendor_id = opts[:subsystem_vendor_id] @subsystem_device_id = opts[:subsystem_device_id] end
Syntax: vendor vendor_name
device device_name <-- single tab subvendor subdevice subsystem_name <-- two tabs
@return [Array] - array of lines of the pci database
# File lib/compute_unit/device.rb, line 309 def self.pci_database @pci_database ||= begin IO.foreach(ComputeUnit::PCI_DATABASE_PATH).lazy end end
@return [String] - read a kernel setting using the device_path
@param [String] - the device_path
to read from @param [String] - the name of the kernel file in the device path to read @param [Object] - a default value to use if there is a problem reading the setting
# File lib/compute_unit/device.rb, line 265 def self.read_kernel_setting(device_path, setting, default = 0) path = File.join(device_path, setting) logger.debug("reading kernel file #{path}") value = begin File.read(path).chomp rescue Errno::EINVAL, Errno::EPERM logger.fatal(e.message) default rescue Errno::ENOENT logger.debug("File #{path} does not exist") default rescue Errno::EACCES logger.fatal('Run this command as root or with sudo') default end end
@return [String] - the device number ie. 1002 (can be different from vendor)
# File lib/compute_unit/device.rb, line 235 def self.subsystem_device(device_path) read_kernel_setting(device_path, 'subsystem_device', '').slice(2, 6) end
@return [String] - the name of the device @param device_id
[String] - the device id of the device @param subsystem_device_id
[String] - the subsystem_device_id
of the device @param vendor_id [String] - the subsystem vendor id
# File lib/compute_unit/device.rb, line 330 def self.subsystem_device_lookup(device_id, subsystem_device_id, vendor_id) # "\t\t1002 687f Vega 10 XT [Radeon RX Vega 64]\n" p = manual_device_lookup(device_id, subsystem_device_id, vendor_id) return p if p re = Regexp.new(/\A\t\t#{vendor_id}\s+#{subsystem_device_id}/) d = pci_database.find do |line| re.match(line) rescue ArgumentError next end return d unless d name = d[/\t\t\w+\s+\w+\s+(.*)\n/, 1] name[/.*\[(.*)\]/, 1] || name if name end
@return [String] - the vendor number ie. 1002 (can be different from vendor) example: XFX is the reseller of the AMD chips
# File lib/compute_unit/device.rb, line 241 def self.subsystem_vendor(device_path) read_kernel_setting(device_path, 'subsystem_vendor', '').slice(2, 6) end
@return [String] - the name of the subsystem vendor with a possible translation
# File lib/compute_unit/device.rb, line 348 def self.subsystem_vendor_lookup(vendor_id) # "1002 Advanced Micro Devices, Inc. [AMD/ATI]\n" vendor_lookup(vendor_id) end
@return [String] - a sha1 digest that represents the pci devices found on the system
# File lib/compute_unit/device.rb, line 298 def self.system_checksum @system_checksum ||= Dir.chdir(ComputeUnit::SYS_DEVICE_PATH) do Digest::SHA1.hexdigest(Dir.glob('*').sort.join) end end
@return [String] - the name of the subsystem vendor with a possible translation
# File lib/compute_unit/device.rb, line 365 def self.vendor_lookup(vendor_id) # "1002 Advanced Micro Devices, Inc. [AMD/ATI]\n" re = Regexp.new(/\A#{vendor_id}/) d = pci_database.find do |line| re.match(line) rescue ArgumentError next end return manual_vendor_lookup(vendor_id) unless d name = d[/\w+\s+(.*)\n/, 1] name_translation(name) end
@return [String] - read a kernel setting using the device_path
@param [String] - the device_path
to write to @param [String] - the name of the kernel file in the device path to read
# File lib/compute_unit/device.rb, line 285 def self.write_kernel_setting(device_path, setting, value) path = File.join(device_path, setting) File.write(path, value) read_kernel_setting(device_path, setting) rescue Errno::EINVAL, Errno::EPERM => e logger.fatal(e.message) rescue Errno::ENOENT logger.warn("File #{path} does not exist") rescue Errno::EACCES logger.fatal('Run this command as root or with sudo') end
Public Instance Methods
@return [String] - base hwmon path of the device @note this is used mainly for easier mocking
# File lib/compute_unit/device.rb, line 159 def base_hwmon_path File.join(device_path, 'hwmon') end
# File lib/compute_unit/device.rb, line 173 def expired_metadata? timestamp + CACHE_TIMEOUT < Time.now.to_i end
@return [String] - the name of the device model (sometimes not specific)
# File lib/compute_unit/device.rb, line 121 def generic_model @generic_model ||= begin name = self.class.device_lookup(device_id) name[/\[?(.*)\]?/, 1] if name end end
@return [String] - the directory path to the hwmon dir for this device @note this path can be different for each device @note returns the base_hwmon_path
if no path is found
# File lib/compute_unit/device.rb, line 166 def hwmon_path @hwmon_path ||= begin paths = Dir.glob(File.join(base_hwmon_path, '*')) paths.first || base_hwmon_path end end
@return [String] - writes a 0 to the rom file, therby locking it must be root
# File lib/compute_unit/device.rb, line 56 def lock_rom File.write(rom_path, '0') if File.exist?(rom_path) end
# File lib/compute_unit/device.rb, line 128 def read_file(path, default = nil) File.read(path).chomp rescue Errno::EINVAL, Errno::EPERM default rescue Errno::ENOENT logger.debug("File #{path} does not exist, using defaults") default rescue Errno::EACCES logger.fatal('run this command as root or with sudo, using default value') default end
@param item [String] - the name of the hwmon file to read from @param default [Object] - the default value to return if the file is empty or not readable @return [String] - the value of the item looked up
# File lib/compute_unit/device.rb, line 143 def read_hwmon_data(item, default = nil) path = File.join(hwmon_path, item) read_file(path, default) end
@return [String] - read a kernel setting using the device_path
@param [String] - the name of the kernel file in the device path to read @param [Object] - a default value to use if there is a problem reading the setting
# File lib/compute_unit/device.rb, line 75 def read_kernel_setting(setting, default = 0) self.class.read_kernel_setting(device_path, setting, default) end
@return [String::IO] - the contents of the rom file
# File lib/compute_unit/device.rb, line 61 def rom_data return unless File.exist?(rom_path) begin unlock_rom IO.read(rom_path, mode: 'rb') ensure lock_rom end end
@return [String] - the path to the rom file if available
# File lib/compute_unit/device.rb, line 44 def rom_path @rom_path ||= File.join(device_path, 'rom') end
@return [String] - the name of the device model (specific name)
# File lib/compute_unit/device.rb, line 107 def sysfs_model_name name = self.class.subsystem_device_lookup(device_id, subsystem_device_id, subsystem_vendor_id) m = name[/\[?(.*)\]?/, 1] if name m || generic_model end
# File lib/compute_unit/device.rb, line 35 def to_h { chip_make: make, make: vendor, model: model, device_id: device_id, vendor_id: device_vendor_id, subsystem_device_id: subsystem_device_id, subsystem_vendor_id: subsystem_vendor_id, device_class: device_class_id } end
# File lib/compute_unit/device.rb, line 177 def to_json(c = nil) to_h.to_json(c) end
@return [String] - writes a 1 to the rom file, therby unlocking it for reading must be root
# File lib/compute_unit/device.rb, line 50 def unlock_rom File.write(rom_path, '1') if File.exist?(rom_path) end
@param item [String] - the name of the hwmon file to write to @param value [String] - the value you want to write to the hwmon file
# File lib/compute_unit/device.rb, line 150 def write_hwmon_data(item, value) File.write(File.join(hwmon_path, item), value) rescue Errno::EACCES => e logger.info(e.message) check_for_root end
@return [String] - a reading of the kernel setting using the device_path
@param value [String] - the value to assign to the setting @param setting [String] - the name of the kernel file in the device path to read
# File lib/compute_unit/device.rb, line 82 def write_kernel_setting(setting, value) self.class.write_kernel_setting(device_path, setting, value) end