fastfetch.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import json
  2. import logging
  3. import platform
  4. import subprocess
  5. from gpustack.detectors.base import SystemInfoDetector
  6. from gpustack.schemas.workers import (
  7. CPUInfo,
  8. KernelInfo,
  9. MemoryInfo,
  10. MountPoint,
  11. OperatingSystemInfo,
  12. SwapInfo,
  13. SystemInfo,
  14. UptimeInfo,
  15. )
  16. from gpustack.utils.compat_importlib import pkg_resources
  17. logger = logging.getLogger(__name__)
  18. class Fastfetch(SystemInfoDetector):
  19. def is_available(self) -> bool:
  20. try:
  21. self._run_command(self._command_version(), parse_output=False)
  22. return True
  23. except Exception as e:
  24. logger.warning(f"Fastfetch is not available: {e}")
  25. return False
  26. def gather_system_info(self) -> SystemInfo: # noqa: C901
  27. command = self._command_gather_system()
  28. results = self._run_command(command)
  29. system_info = SystemInfo()
  30. for result in results:
  31. typ = result.get("type")
  32. r = result.get("result")
  33. if r is None:
  34. continue
  35. if typ == "OS":
  36. system_info.os = OperatingSystemInfo(
  37. name=self._get_value(r, "name"),
  38. version=self._get_value(r, "version"),
  39. )
  40. elif typ == "Kernel":
  41. k = KernelInfo(
  42. name=self._get_value(r, "name"),
  43. release=self._get_value(r, "release"),
  44. version=self._get_value(r, "version"),
  45. architecture=self._get_value(r, "architecture"),
  46. )
  47. system_info.kernel = k
  48. elif typ == "Uptime":
  49. uptime_value = self._get_value(r, "uptime")
  50. uptime = (
  51. uptime_value / 1000
  52. if uptime_value and isinstance(uptime_value, (int, float))
  53. else 0
  54. )
  55. system_info.uptime = UptimeInfo(
  56. uptime=uptime,
  57. boot_time=self._get_value(r, "bootTime"),
  58. )
  59. elif typ == "CPU":
  60. total = self._get_value(r, "cores", "online")
  61. if system_info.cpu is None:
  62. system_info.cpu = CPUInfo(
  63. total=total,
  64. )
  65. else:
  66. system_info.cpu.total = total
  67. elif typ == "CPUUsage":
  68. core_count = len(r)
  69. sum = 0
  70. for usage_per_core in r:
  71. sum += usage_per_core
  72. utilization_rate = sum / core_count if core_count > 0 else 0
  73. if system_info.cpu is None:
  74. system_info.cpu = CPUInfo(
  75. utilization_rate=utilization_rate,
  76. )
  77. else:
  78. system_info.cpu.utilization_rate = utilization_rate
  79. elif typ == "Memory":
  80. total = self._get_value(r, "total") or 0
  81. used = self._get_value(r, "used") or 0
  82. utilization_rate = used / total * 100 if total > 0 else 0
  83. system_info.memory = MemoryInfo(
  84. total=total,
  85. used=used,
  86. utilization_rate=utilization_rate,
  87. )
  88. elif typ == "Swap":
  89. total = self._get_value(r, "total") or 0
  90. used = self._get_value(r, "used") or 0
  91. utilization_rate = used / total * 100 if total > 0 else 0
  92. system_info.swap = SwapInfo(
  93. total=total,
  94. used=used,
  95. utilization_rate=utilization_rate,
  96. )
  97. elif typ == "Disk":
  98. mountpoints = []
  99. for disk in r:
  100. mountpoints.append(
  101. MountPoint(
  102. name=self._get_value(disk, "name"),
  103. mount_point=self._get_value(disk, "mountpoint"),
  104. mount_from=self._get_value(disk, "mountFrom"),
  105. total=self._get_value(disk, "bytes", "total") or 0,
  106. used=self._get_value(disk, "bytes", "used") or 0,
  107. free=self._get_value(disk, "bytes", "free") or 0,
  108. available=self._get_value(disk, "bytes", "available") or 0,
  109. )
  110. )
  111. system_info.filesystem = mountpoints
  112. return system_info
  113. @staticmethod
  114. def _run_command(command, parse_output=True):
  115. result = None
  116. try:
  117. result = subprocess.run(
  118. command, capture_output=True, text=True, check=True, encoding="utf-8"
  119. )
  120. if result.returncode != 0:
  121. raise Exception(f"Unexpected return code: {result.returncode}")
  122. output = result.stdout
  123. if output == "" or output is None:
  124. raise Exception(f"Output is empty, return code: {result.returncode}")
  125. except Exception as e:
  126. error_message = f"Failed to execute {command}: {e}"
  127. if result:
  128. error_message += f", stdout: {result.stdout}, stderr: {result.stderr}"
  129. raise Exception(error_message)
  130. if not parse_output:
  131. return output
  132. try:
  133. parsed_json = json.loads(output)
  134. return parsed_json
  135. except Exception as e:
  136. raise Exception(
  137. f"Failed to parse the output of {command}: {e}, output: {output}"
  138. )
  139. @staticmethod
  140. def _command_executable_path():
  141. command = "fastfetch"
  142. if platform.system().lower() == "windows":
  143. command += ".exe"
  144. with pkg_resources.path(
  145. "gpustack.third_party.bin.fastfetch", command
  146. ) as executable_path:
  147. return str(executable_path)
  148. def _command_version(self):
  149. executable_path = self._command_executable_path()
  150. executable_command = [
  151. executable_path,
  152. "--version",
  153. ]
  154. return executable_command
  155. def _command_gather_gpu(self):
  156. with pkg_resources.path(
  157. "gpustack.detectors.fastfetch", "config_gpu.jsonc"
  158. ) as config_path:
  159. executable_path = self._command_executable_path()
  160. executable_command = [
  161. executable_path,
  162. "--config",
  163. str(config_path),
  164. "--gpu-driver-specific",
  165. "true",
  166. "--gpu-temp",
  167. "true",
  168. "--gpu-detection-method",
  169. "pci",
  170. "--format",
  171. "json",
  172. ]
  173. return executable_command
  174. def _command_gather_system(self):
  175. with pkg_resources.path(
  176. "gpustack.detectors.fastfetch", "config_system_info.jsonc"
  177. ) as config_path:
  178. executable_path = self._command_executable_path()
  179. executable_command = [
  180. executable_path,
  181. "--config",
  182. str(config_path),
  183. "--format",
  184. "json",
  185. ]
  186. return executable_command
  187. @staticmethod
  188. def _get_value(input: dict, *keys):
  189. current_value = input
  190. for key in keys:
  191. if isinstance(current_value, dict) and key in current_value:
  192. current_value = current_value[key]
  193. else:
  194. return None
  195. return current_value