sysinfo.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. # Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. '''This module returns system information.'''
  16. import os
  17. import sys
  18. import re
  19. import subprocess
  20. import os.path
  21. import platform
  22. import time
  23. class SysInfo:
  24. def __init__(self):
  25. self._num_processors = -1
  26. self._endianness = 'Unknown'
  27. self._hostname = ''
  28. self._platform_name = 'Unknown'
  29. self._platform_version = 'Unknown'
  30. self._platform_machine = 'Unknown'
  31. self._platform_is_smp = False
  32. self._uptime = -1
  33. self._loadavg = [-1.0, -1.0, -1.0]
  34. self._mem_total = -1
  35. self._mem_free = -1
  36. self._mem_cached = -1
  37. self._mem_buffers = -1
  38. self._mem_swap_total = -1
  39. self._mem_swap_free = -1
  40. self._platform_distro = 'Unknown'
  41. self._net_interfaces = 'Unknown\n'
  42. self._net_routing_table = 'Unknown\n'
  43. self._net_stats = 'Unknown\n'
  44. self._net_connections = 'Unknown\n'
  45. def get_num_processors(self):
  46. """Returns the number of processors. This is the number of
  47. hyperthreads when hyper-threading is enabled.
  48. """
  49. return self._num_processors
  50. def get_endianness(self):
  51. """Returns 'big' or 'little'."""
  52. return self._endianness
  53. def get_platform_hostname(self):
  54. """Returns the hostname of the system."""
  55. return self._hostname
  56. def get_platform_name(self):
  57. """Returns the platform name (uname -s)."""
  58. return self._platform_name
  59. def get_platform_version(self):
  60. """Returns the platform version (uname -v)."""
  61. return self._platform_version
  62. def get_platform_machine(self):
  63. """Returns the platform machine architecture."""
  64. return self._platform_machine
  65. def get_platform_is_smp(self):
  66. """Returns True if an SMP kernel is being used, False otherwise."""
  67. return self._platform_is_smp
  68. def get_platform_distro(self):
  69. """Returns the name of the OS distribution in use."""
  70. return self._platform_distro
  71. def get_uptime(self):
  72. """Returns the uptime in seconds."""
  73. return self._uptime
  74. def get_loadavg(self):
  75. """Returns the load average as 3 floating point values in an array."""
  76. return self._loadavg
  77. def get_mem_total(self):
  78. """Returns the total amount of memory in bytes."""
  79. return self._mem_total
  80. def get_mem_free(self):
  81. """Returns the amount of free memory in bytes."""
  82. return self._mem_free
  83. def get_mem_cached(self):
  84. """Returns the amount of cached memory in bytes."""
  85. return self._mem_cached
  86. def get_mem_buffers(self):
  87. """Returns the amount of buffer in bytes."""
  88. return self._mem_buffers
  89. def get_mem_swap_total(self):
  90. """Returns the total amount of swap in bytes."""
  91. return self._mem_swap_total
  92. def get_mem_swap_free(self):
  93. """Returns the amount of free swap in bytes."""
  94. return self._mem_swap_free
  95. def get_net_interfaces(self):
  96. """Returns information about network interfaces (as a multi-line string)."""
  97. return self._net_interfaces
  98. def get_net_routing_table(self):
  99. """Returns information about network routing table (as a multi-line string)."""
  100. return self._net_routing_table
  101. def get_net_stats(self):
  102. """Returns network statistics (as a multi-line string)."""
  103. return self._net_stats
  104. def get_net_connections(self):
  105. """Returns network connection information (as a multi-line string)."""
  106. return self._net_connections
  107. class SysInfoPOSIX(SysInfo):
  108. """Common POSIX implementation of the SysInfo class.
  109. See the SysInfo class documentation for more information.
  110. """
  111. def __init__(self):
  112. super().__init__()
  113. self._num_processors = os.sysconf('SC_NPROCESSORS_CONF')
  114. self._endianness = sys.byteorder
  115. u = os.uname()
  116. self._platform_name = u[0]
  117. self._platform_version = u[2]
  118. self._platform_machine = u[4]
  119. class SysInfoLinux(SysInfoPOSIX):
  120. """Linux implementation of the SysInfo class.
  121. See the SysInfo class documentation for more information.
  122. """
  123. def __init__(self):
  124. super().__init__()
  125. with open('/proc/sys/kernel/hostname') as f:
  126. self._hostname = f.read().strip()
  127. with open('/proc/version') as f:
  128. self._platform_is_smp = ' SMP ' in f.read().strip()
  129. with open('/proc/uptime') as f:
  130. u = f.read().strip().split(' ')
  131. if len(u) > 1:
  132. self._uptime = int(round(float(u[0])))
  133. with open('/proc/loadavg') as f:
  134. l = f.read().strip().split(' ')
  135. if len(l) >= 3:
  136. self._loadavg = [float(l[0]), float(l[1]), float(l[2])]
  137. with open('/proc/meminfo') as f:
  138. m = f.readlines()
  139. for line in m:
  140. r = re.match('^MemTotal:\s+(.*)\s*kB', line)
  141. if r:
  142. self._mem_total = int(r.group(1).strip()) * 1024
  143. continue
  144. r = re.match('^MemFree:\s+(.*)\s*kB', line)
  145. if r:
  146. self._mem_free = int(r.group(1).strip()) * 1024
  147. continue
  148. r = re.match('^Cached:\s+(.*)\s*kB', line)
  149. if r:
  150. self._mem_cached = int(r.group(1).strip()) * 1024
  151. continue
  152. r = re.match('^Buffers:\s+(.*)\s*kB', line)
  153. if r:
  154. self._mem_buffers = int(r.group(1).strip()) * 1024
  155. continue
  156. r = re.match('^SwapTotal:\s+(.*)\s*kB', line)
  157. if r:
  158. self._mem_swap_total = int(r.group(1).strip()) * 1024
  159. continue
  160. r = re.match('^SwapFree:\s+(.*)\s*kB', line)
  161. if r:
  162. self._mem_swap_free = int(r.group(1).strip()) * 1024
  163. continue
  164. self._platform_distro = None
  165. try:
  166. s = subprocess.check_output(['lsb_release', '-a'])
  167. for line in s.decode('utf-8').split('\n'):
  168. r = re.match('^Description:(.*)', line)
  169. if r:
  170. self._platform_distro = r.group(1).strip()
  171. break
  172. except (subprocess.CalledProcessError, OSError):
  173. pass
  174. if self._platform_distro is None:
  175. files = ['/etc/debian_release',
  176. '/etc/debian_version',
  177. '/etc/SuSE-release',
  178. '/etc/UnitedLinux-release',
  179. '/etc/mandrake-release',
  180. '/etc/gentoo-release',
  181. '/etc/fedora-release',
  182. '/etc/redhat-release',
  183. '/etc/redhat_version',
  184. '/etc/slackware-release',
  185. '/etc/slackware-version',
  186. '/etc/arch-release',
  187. '/etc/lsb-release',
  188. '/etc/mageia-release']
  189. for fn in files:
  190. if os.path.exists(fn):
  191. with open(fn) as f:
  192. self._platform_distro = f.read().strip()
  193. break
  194. if self._platform_distro is None:
  195. self._platform_distro = 'Unknown'
  196. try:
  197. s = subprocess.check_output(['ip', 'addr'])
  198. self._net_interfaces = s.decode('utf-8')
  199. except (subprocess.CalledProcessError, OSError):
  200. self._net_interfaces = 'Warning: "ip addr" command failed.\n'
  201. try:
  202. s = subprocess.check_output(['ip', 'route'])
  203. self._net_routing_table = s.decode('utf-8')
  204. self._net_routing_table += '\n'
  205. s = subprocess.check_output(['ip', '-f', 'inet6', 'route'])
  206. self._net_routing_table += s.decode('utf-8')
  207. except (subprocess.CalledProcessError, OSError):
  208. self._net_routing_table = 'Warning: "ip route" or "ip -f inet6 route" command failed.\n'
  209. try:
  210. s = subprocess.check_output(['netstat', '-s'])
  211. self._net_stats = s.decode('utf-8')
  212. except (subprocess.CalledProcessError, OSError):
  213. self._net_stats = 'Warning: "netstat -s" command failed.\n'
  214. try:
  215. s = subprocess.check_output(['netstat', '-apn'])
  216. self._net_connections = s.decode('utf-8')
  217. except (subprocess.CalledProcessError, OSError):
  218. self._net_connections = 'Warning: "netstat -apn" command failed.\n'
  219. class SysInfoBSD(SysInfoPOSIX):
  220. """Common BSD implementation of the SysInfo class.
  221. See the SysInfo class documentation for more information.
  222. """
  223. def __init__(self):
  224. super().__init__()
  225. try:
  226. s = subprocess.check_output(['hostname'])
  227. self._hostname = s.decode('utf-8').strip()
  228. except (subprocess.CalledProcessError, OSError):
  229. pass
  230. try:
  231. s = subprocess.check_output(['sysctl', '-n', 'hw.physmem'])
  232. self._mem_total = int(s.decode('utf-8').strip())
  233. except (subprocess.CalledProcessError, OSError):
  234. pass
  235. self._platform_distro = self._platform_name + ' ' + self._platform_version
  236. try:
  237. s = subprocess.check_output(['ifconfig'])
  238. self._net_interfaces = s.decode('utf-8')
  239. except (subprocess.CalledProcessError, OSError):
  240. self._net_interfaces = 'Warning: "ifconfig" command failed.\n'
  241. try:
  242. s = subprocess.check_output(['netstat', '-s'])
  243. self._net_stats = s.decode('utf-8')
  244. except (subprocess.CalledProcessError, OSError):
  245. self._net_stats = 'Warning: "netstat -s" command failed.\n'
  246. try:
  247. s = subprocess.check_output(['netstat', '-an'])
  248. self._net_connections = s.decode('utf-8')
  249. except (subprocess.CalledProcessError, OSError):
  250. self._net_connections = 'Warning: "netstat -an" command failed.\n'
  251. class SysInfoOpenBSD(SysInfoBSD):
  252. """OpenBSD implementation of the SysInfo class.
  253. See the SysInfo class documentation for more information.
  254. """
  255. def __init__(self):
  256. super().__init__()
  257. # Don't know how to gather these
  258. self._platform_is_smp = False
  259. self._mem_cached = -1
  260. self._mem_buffers = -1
  261. try:
  262. s = subprocess.check_output(['sysctl', '-n', 'kern.boottime'])
  263. t = s.decode('utf-8').strip()
  264. sec = time.time() - int(t)
  265. self._uptime = int(round(sec))
  266. except (subprocess.CalledProcessError, OSError):
  267. pass
  268. try:
  269. s = subprocess.check_output(['sysctl', '-n', 'vm.loadavg'])
  270. l = s.decode('utf-8').strip().split(' ')
  271. if len(l) >= 3:
  272. self._loadavg = [float(l[0]), float(l[1]), float(l[2])]
  273. except (subprocess.CalledProcessError, OSError):
  274. pass
  275. try:
  276. s = subprocess.check_output(['vmstat'])
  277. lines = s.decode('utf-8').split('\n')
  278. v = re.split('\s+', lines[2])
  279. used = int(v[4]) * 1024
  280. self._mem_free = self._mem_total - used
  281. except (subprocess.CalledProcessError, OSError):
  282. pass
  283. try:
  284. s = subprocess.check_output(['swapctl', '-s', '-k'])
  285. l = s.decode('utf-8').strip()
  286. r = re.match('^total: (\d+) 1K-blocks allocated, (\d+) used, (\d+) available', l)
  287. if r:
  288. self._mem_swap_total = int(r.group(1).strip()) * 1024
  289. self._mem_swap_free = int(r.group(3).strip()) * 1024
  290. except (subprocess.CalledProcessError, OSError):
  291. pass
  292. try:
  293. s = subprocess.check_output(['route', '-n', 'show'])
  294. self._net_routing_table = s.decode('utf-8')
  295. except (subprocess.CalledProcessError, OSError):
  296. self._net_routing_table = 'Warning: "route -n show" command failed.\n'
  297. class SysInfoFreeBSD(SysInfoBSD):
  298. """FreeBSD implementation of the SysInfo class.
  299. See the SysInfo class documentation for more information.
  300. """
  301. def __init__(self):
  302. super().__init__()
  303. # Don't know how to gather these
  304. self._mem_cached = -1
  305. self._mem_buffers = -1
  306. try:
  307. s = subprocess.check_output(['sysctl', '-n', 'kern.smp.active'])
  308. self._platform_is_smp = int(s.decode('utf-8').strip()) > 0
  309. except (subprocess.CalledProcessError, OSError):
  310. pass
  311. try:
  312. s = subprocess.check_output(['sysctl', '-n', 'kern.boottime'])
  313. t = s.decode('utf-8').strip()
  314. r = re.match('^\{\s+sec\s+\=\s+(\d+),.*', t)
  315. if r:
  316. sec = time.time() - int(r.group(1))
  317. self._uptime = int(round(sec))
  318. except (subprocess.CalledProcessError, OSError):
  319. pass
  320. try:
  321. s = subprocess.check_output(['sysctl', '-n', 'vm.loadavg'])
  322. l = s.decode('utf-8').strip()
  323. r = re.match('^\{(.*)\}$', l)
  324. if r:
  325. la = r.group(1).strip().split(' ')
  326. else:
  327. la = l.split(' ')
  328. if len(la) >= 3:
  329. self._loadavg = [float(la[0]), float(la[1]), float(la[2])]
  330. except (subprocess.CalledProcessError, OSError):
  331. pass
  332. try:
  333. s = subprocess.check_output(['vmstat', '-H'])
  334. lines = s.decode('utf-8').split('\n')
  335. v = re.split('\s+', lines[2])
  336. used = int(v[4]) * 1024
  337. self._mem_free = self._mem_total - used
  338. except (subprocess.CalledProcessError, OSError):
  339. pass
  340. try:
  341. s = subprocess.check_output(['swapctl', '-s', '-k'])
  342. l = s.decode('utf-8').strip()
  343. r = re.match('^Total:\s+(\d+)\s+(\d+)', l)
  344. if r:
  345. self._mem_swap_total = int(r.group(1).strip()) * 1024
  346. self._mem_swap_free = self._mem_swap_total - (int(r.group(2).strip()) * 1024)
  347. except (subprocess.CalledProcessError, OSError):
  348. pass
  349. try:
  350. s = subprocess.check_output(['netstat', '-nr'])
  351. self._net_routing_table = s.decode('utf-8')
  352. except (subprocess.CalledProcessError, OSError):
  353. self._net_connections = 'Warning: "netstat -nr" command failed.\n'
  354. class SysInfoTestcase(SysInfo):
  355. def __init__(self):
  356. super().__init__()
  357. self._endianness = 'bigrastafarian'
  358. self._platform_name = 'b10test'
  359. self._uptime = 131072
  360. def SysInfoFromFactory():
  361. osname = platform.system()
  362. if osname == 'Linux':
  363. return SysInfoLinux()
  364. elif osname == 'OpenBSD':
  365. return SysInfoOpenBSD()
  366. elif osname == 'FreeBSD':
  367. return SysInfoFreeBSD()
  368. elif osname == 'BIND10Testcase':
  369. return SysInfoTestcase()
  370. else:
  371. return SysInfo()