/lib/ansible/module_utils/facts.py

https://github.com/ajanthanm/ansible · Python · 2371 lines · 1776 code · 264 blank · 331 comment · 531 complexity · a9c77d38aded62bddd620d444873d9fa MD5 · raw file

Large files are truncated click here to view the full file

  1. # (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
  2. #
  3. # This file is part of Ansible
  4. #
  5. # Ansible is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # Ansible is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
  17. import os
  18. import array
  19. import errno
  20. import fcntl
  21. import fnmatch
  22. import glob
  23. import platform
  24. import re
  25. import signal
  26. import socket
  27. import struct
  28. import datetime
  29. import getpass
  30. import ConfigParser
  31. import StringIO
  32. try:
  33. import selinux
  34. HAVE_SELINUX=True
  35. except ImportError:
  36. HAVE_SELINUX=False
  37. try:
  38. import json
  39. except ImportError:
  40. import simplejson as json
  41. # --------------------------------------------------------------
  42. # timeout function to make sure some fact gathering
  43. # steps do not exceed a time limit
  44. class TimeoutError(Exception):
  45. pass
  46. def timeout(seconds=10, error_message="Timer expired"):
  47. def decorator(func):
  48. def _handle_timeout(signum, frame):
  49. raise TimeoutError(error_message)
  50. def wrapper(*args, **kwargs):
  51. signal.signal(signal.SIGALRM, _handle_timeout)
  52. signal.alarm(seconds)
  53. try:
  54. result = func(*args, **kwargs)
  55. finally:
  56. signal.alarm(0)
  57. return result
  58. return wrapper
  59. return decorator
  60. # --------------------------------------------------------------
  61. class Facts(object):
  62. """
  63. This class should only attempt to populate those facts that
  64. are mostly generic to all systems. This includes platform facts,
  65. service facts (eg. ssh keys or selinux), and distribution facts.
  66. Anything that requires extensive code or may have more than one
  67. possible implementation to establish facts for a given topic should
  68. subclass Facts.
  69. """
  70. _I386RE = re.compile(r'i[3456]86')
  71. # For the most part, we assume that platform.dist() will tell the truth.
  72. # This is the fallback to handle unknowns or exceptions
  73. OSDIST_DICT = { '/etc/redhat-release': 'RedHat',
  74. '/etc/vmware-release': 'VMwareESX',
  75. '/etc/openwrt_release': 'OpenWrt',
  76. '/etc/system-release': 'OtherLinux',
  77. '/etc/alpine-release': 'Alpine',
  78. '/etc/release': 'Solaris',
  79. '/etc/arch-release': 'Archlinux',
  80. '/etc/SuSE-release': 'SuSE',
  81. '/etc/gentoo-release': 'Gentoo',
  82. '/etc/os-release': 'Debian' }
  83. SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' }
  84. # A list of dicts. If there is a platform with more than one
  85. # package manager, put the preferred one last. If there is an
  86. # ansible module, use that as the value for the 'name' key.
  87. PKG_MGRS = [ { 'path' : '/usr/bin/yum', 'name' : 'yum' },
  88. { 'path' : '/usr/bin/apt-get', 'name' : 'apt' },
  89. { 'path' : '/usr/bin/zypper', 'name' : 'zypper' },
  90. { 'path' : '/usr/sbin/urpmi', 'name' : 'urpmi' },
  91. { 'path' : '/usr/bin/pacman', 'name' : 'pacman' },
  92. { 'path' : '/bin/opkg', 'name' : 'opkg' },
  93. { 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' },
  94. { 'path' : '/opt/local/bin/port', 'name' : 'macports' },
  95. { 'path' : '/sbin/apk', 'name' : 'apk' },
  96. { 'path' : '/usr/sbin/pkg', 'name' : 'pkgng' },
  97. { 'path' : '/usr/sbin/swlist', 'name' : 'SD-UX' },
  98. { 'path' : '/usr/bin/emerge', 'name' : 'portage' },
  99. { 'path' : '/usr/sbin/pkgadd', 'name' : 'svr4pkg' },
  100. { 'path' : '/usr/bin/pkg', 'name' : 'pkg' },
  101. ]
  102. def __init__(self):
  103. self.facts = {}
  104. self.get_platform_facts()
  105. self.get_distribution_facts()
  106. self.get_cmdline()
  107. self.get_public_ssh_host_keys()
  108. self.get_selinux_facts()
  109. self.get_pkg_mgr_facts()
  110. self.get_lsb_facts()
  111. self.get_date_time_facts()
  112. self.get_user_facts()
  113. self.get_local_facts()
  114. self.get_env_facts()
  115. def populate(self):
  116. return self.facts
  117. # Platform
  118. # platform.system() can be Linux, Darwin, Java, or Windows
  119. def get_platform_facts(self):
  120. self.facts['system'] = platform.system()
  121. self.facts['kernel'] = platform.release()
  122. self.facts['machine'] = platform.machine()
  123. self.facts['python_version'] = platform.python_version()
  124. self.facts['fqdn'] = socket.getfqdn()
  125. self.facts['hostname'] = platform.node().split('.')[0]
  126. self.facts['nodename'] = platform.node()
  127. self.facts['domain'] = '.'.join(self.facts['fqdn'].split('.')[1:])
  128. arch_bits = platform.architecture()[0]
  129. self.facts['userspace_bits'] = arch_bits.replace('bit', '')
  130. if self.facts['machine'] == 'x86_64':
  131. self.facts['architecture'] = self.facts['machine']
  132. if self.facts['userspace_bits'] == '64':
  133. self.facts['userspace_architecture'] = 'x86_64'
  134. elif self.facts['userspace_bits'] == '32':
  135. self.facts['userspace_architecture'] = 'i386'
  136. elif Facts._I386RE.search(self.facts['machine']):
  137. self.facts['architecture'] = 'i386'
  138. if self.facts['userspace_bits'] == '64':
  139. self.facts['userspace_architecture'] = 'x86_64'
  140. elif self.facts['userspace_bits'] == '32':
  141. self.facts['userspace_architecture'] = 'i386'
  142. else:
  143. self.facts['architecture'] = self.facts['machine']
  144. if self.facts['system'] == 'Linux':
  145. self.get_distribution_facts()
  146. elif self.facts['system'] == 'AIX':
  147. rc, out, err = module.run_command("/usr/sbin/bootinfo -p")
  148. data = out.split('\n')
  149. self.facts['architecture'] = data[0]
  150. def get_local_facts(self):
  151. fact_path = module.params.get('fact_path', None)
  152. if not fact_path or not os.path.exists(fact_path):
  153. return
  154. local = {}
  155. for fn in sorted(glob.glob(fact_path + '/*.fact')):
  156. # where it will sit under local facts
  157. fact_base = os.path.basename(fn).replace('.fact','')
  158. if os.access(fn, os.X_OK):
  159. # run it
  160. # try to read it as json first
  161. # if that fails read it with ConfigParser
  162. # if that fails, skip it
  163. rc, out, err = module.run_command(fn)
  164. else:
  165. out = open(fn).read()
  166. # load raw json
  167. fact = 'loading %s' % fact_base
  168. try:
  169. fact = json.loads(out)
  170. except ValueError, e:
  171. # load raw ini
  172. cp = ConfigParser.ConfigParser()
  173. try:
  174. cp.readfp(StringIO.StringIO(out))
  175. except ConfigParser.Error, e:
  176. fact="error loading fact - please check content"
  177. else:
  178. fact = {}
  179. #print cp.sections()
  180. for sect in cp.sections():
  181. if sect not in fact:
  182. fact[sect] = {}
  183. for opt in cp.options(sect):
  184. val = cp.get(sect, opt)
  185. fact[sect][opt]=val
  186. local[fact_base] = fact
  187. if not local:
  188. return
  189. self.facts['local'] = local
  190. # platform.dist() is deprecated in 2.6
  191. # in 2.6 and newer, you should use platform.linux_distribution()
  192. def get_distribution_facts(self):
  193. # A list with OS Family members
  194. OS_FAMILY = dict(
  195. RedHat = 'RedHat', Fedora = 'RedHat', CentOS = 'RedHat', Scientific = 'RedHat',
  196. SLC = 'RedHat', Ascendos = 'RedHat', CloudLinux = 'RedHat', PSBM = 'RedHat',
  197. OracleLinux = 'RedHat', OVS = 'RedHat', OEL = 'RedHat', Amazon = 'RedHat',
  198. XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', SLES = 'Suse',
  199. SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo',
  200. Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
  201. Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris',
  202. SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin',
  203. FreeBSD = 'FreeBSD', HPUX = 'HP-UX'
  204. )
  205. if self.facts['system'] == 'AIX':
  206. self.facts['distribution'] = 'AIX'
  207. rc, out, err = module.run_command("/usr/bin/oslevel")
  208. data = out.split('.')
  209. self.facts['distribution_version'] = data[0]
  210. self.facts['distribution_release'] = data[1]
  211. elif self.facts['system'] == 'HP-UX':
  212. self.facts['distribution'] = 'HP-UX'
  213. rc, out, err = module.run_command("/usr/sbin/swlist |egrep 'HPUX.*OE.*[AB].[0-9]+\.[0-9]+'", use_unsafe_shell=True)
  214. data = re.search('HPUX.*OE.*([AB].[0-9]+\.[0-9]+)\.([0-9]+).*', out)
  215. if data:
  216. self.facts['distribution_version'] = data.groups()[0]
  217. self.facts['distribution_release'] = data.groups()[1]
  218. elif self.facts['system'] == 'Darwin':
  219. self.facts['distribution'] = 'MacOSX'
  220. rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion")
  221. data = out.split()[-1]
  222. self.facts['distribution_version'] = data
  223. elif self.facts['system'] == 'FreeBSD':
  224. self.facts['distribution'] = 'FreeBSD'
  225. self.facts['distribution_release'] = platform.release()
  226. self.facts['distribution_version'] = platform.version()
  227. elif self.facts['system'] == 'OpenBSD':
  228. self.facts['distribution'] = 'OpenBSD'
  229. self.facts['distribution_release'] = platform.release()
  230. rc, out, err = module.run_command("/sbin/sysctl -n kern.version")
  231. match = re.match('OpenBSD\s[0-9]+.[0-9]+-(\S+)\s.*', out)
  232. if match:
  233. self.facts['distribution_version'] = match.groups()[0]
  234. else:
  235. self.facts['distribution_version'] = 'release'
  236. else:
  237. dist = platform.dist()
  238. self.facts['distribution'] = dist[0].capitalize() or 'NA'
  239. self.facts['distribution_version'] = dist[1] or 'NA'
  240. self.facts['distribution_major_version'] = dist[1].split('.')[0] or 'NA'
  241. self.facts['distribution_release'] = dist[2] or 'NA'
  242. # Try to handle the exceptions now ...
  243. for (path, name) in Facts.OSDIST_DICT.items():
  244. if os.path.exists(path) and os.path.getsize(path) > 0:
  245. if self.facts['distribution'] == 'Fedora':
  246. pass
  247. elif name == 'RedHat':
  248. data = get_file_content(path)
  249. if 'Red Hat' in data:
  250. self.facts['distribution'] = name
  251. else:
  252. self.facts['distribution'] = data.split()[0]
  253. elif name == 'OtherLinux':
  254. data = get_file_content(path)
  255. if 'Amazon' in data:
  256. self.facts['distribution'] = 'Amazon'
  257. self.facts['distribution_version'] = data.split()[-1]
  258. elif name == 'OpenWrt':
  259. data = get_file_content(path)
  260. if 'OpenWrt' in data:
  261. self.facts['distribution'] = name
  262. version = re.search('DISTRIB_RELEASE="(.*)"', data)
  263. if version:
  264. self.facts['distribution_version'] = version.groups()[0]
  265. release = re.search('DISTRIB_CODENAME="(.*)"', data)
  266. if release:
  267. self.facts['distribution_release'] = release.groups()[0]
  268. elif name == 'Alpine':
  269. data = get_file_content(path)
  270. self.facts['distribution'] = 'Alpine'
  271. self.facts['distribution_version'] = data
  272. elif name == 'Solaris':
  273. data = get_file_content(path).split('\n')[0]
  274. ora_prefix = ''
  275. if 'Oracle Solaris' in data:
  276. data = data.replace('Oracle ','')
  277. ora_prefix = 'Oracle '
  278. self.facts['distribution'] = data.split()[0]
  279. self.facts['distribution_version'] = data.split()[1]
  280. self.facts['distribution_release'] = ora_prefix + data
  281. elif name == 'SuSE':
  282. data = get_file_content(path).splitlines()
  283. for line in data:
  284. if '=' in line:
  285. self.facts['distribution_release'] = line.split('=')[1].strip()
  286. elif name == 'Debian':
  287. data = get_file_content(path).split('\n')[0]
  288. release = re.search("PRETTY_NAME.+ \(?([^ ]+?)\)?\"", data)
  289. if release:
  290. self.facts['distribution_release'] = release.groups()[0]
  291. else:
  292. self.facts['distribution'] = name
  293. self.facts['os_family'] = self.facts['distribution']
  294. if self.facts['distribution'] in OS_FAMILY:
  295. self.facts['os_family'] = OS_FAMILY[self.facts['distribution']]
  296. def get_cmdline(self):
  297. data = get_file_content('/proc/cmdline')
  298. if data:
  299. self.facts['cmdline'] = {}
  300. for piece in shlex.split(data):
  301. item = piece.split('=', 1)
  302. if len(item) == 1:
  303. self.facts['cmdline'][item[0]] = True
  304. else:
  305. self.facts['cmdline'][item[0]] = item[1]
  306. def get_public_ssh_host_keys(self):
  307. dsa_filename = '/etc/ssh/ssh_host_dsa_key.pub'
  308. rsa_filename = '/etc/ssh/ssh_host_rsa_key.pub'
  309. ecdsa_filename = '/etc/ssh/ssh_host_ecdsa_key.pub'
  310. if self.facts['system'] == 'Darwin':
  311. dsa_filename = '/etc/ssh_host_dsa_key.pub'
  312. rsa_filename = '/etc/ssh_host_rsa_key.pub'
  313. ecdsa_filename = '/etc/ssh_host_ecdsa_key.pub'
  314. dsa = get_file_content(dsa_filename)
  315. rsa = get_file_content(rsa_filename)
  316. ecdsa = get_file_content(ecdsa_filename)
  317. if dsa is None:
  318. dsa = 'NA'
  319. else:
  320. self.facts['ssh_host_key_dsa_public'] = dsa.split()[1]
  321. if rsa is None:
  322. rsa = 'NA'
  323. else:
  324. self.facts['ssh_host_key_rsa_public'] = rsa.split()[1]
  325. if ecdsa is None:
  326. ecdsa = 'NA'
  327. else:
  328. self.facts['ssh_host_key_ecdsa_public'] = ecdsa.split()[1]
  329. def get_pkg_mgr_facts(self):
  330. self.facts['pkg_mgr'] = 'unknown'
  331. for pkg in Facts.PKG_MGRS:
  332. if os.path.exists(pkg['path']):
  333. self.facts['pkg_mgr'] = pkg['name']
  334. if self.facts['system'] == 'OpenBSD':
  335. self.facts['pkg_mgr'] = 'openbsd_pkg'
  336. def get_lsb_facts(self):
  337. lsb_path = module.get_bin_path('lsb_release')
  338. if lsb_path:
  339. rc, out, err = module.run_command([lsb_path, "-a"])
  340. if rc == 0:
  341. self.facts['lsb'] = {}
  342. for line in out.split('\n'):
  343. if len(line) < 1:
  344. continue
  345. value = line.split(':', 1)[1].strip()
  346. if 'LSB Version:' in line:
  347. self.facts['lsb']['release'] = value
  348. elif 'Distributor ID:' in line:
  349. self.facts['lsb']['id'] = value
  350. elif 'Description:' in line:
  351. self.facts['lsb']['description'] = value
  352. elif 'Release:' in line:
  353. self.facts['lsb']['release'] = value
  354. elif 'Codename:' in line:
  355. self.facts['lsb']['codename'] = value
  356. if 'lsb' in self.facts and 'release' in self.facts['lsb']:
  357. self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
  358. elif lsb_path is None and os.path.exists('/etc/lsb-release'):
  359. self.facts['lsb'] = {}
  360. f = open('/etc/lsb-release', 'r')
  361. try:
  362. for line in f.readlines():
  363. value = line.split('=',1)[1].strip()
  364. if 'DISTRIB_ID' in line:
  365. self.facts['lsb']['id'] = value
  366. elif 'DISTRIB_RELEASE' in line:
  367. self.facts['lsb']['release'] = value
  368. elif 'DISTRIB_DESCRIPTION' in line:
  369. self.facts['lsb']['description'] = value
  370. elif 'DISTRIB_CODENAME' in line:
  371. self.facts['lsb']['codename'] = value
  372. finally:
  373. f.close()
  374. else:
  375. return self.facts
  376. if 'lsb' in self.facts and 'release' in self.facts['lsb']:
  377. self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
  378. def get_selinux_facts(self):
  379. if not HAVE_SELINUX:
  380. self.facts['selinux'] = False
  381. return
  382. self.facts['selinux'] = {}
  383. if not selinux.is_selinux_enabled():
  384. self.facts['selinux']['status'] = 'disabled'
  385. else:
  386. self.facts['selinux']['status'] = 'enabled'
  387. try:
  388. self.facts['selinux']['policyvers'] = selinux.security_policyvers()
  389. except OSError, e:
  390. self.facts['selinux']['policyvers'] = 'unknown'
  391. try:
  392. (rc, configmode) = selinux.selinux_getenforcemode()
  393. if rc == 0:
  394. self.facts['selinux']['config_mode'] = Facts.SELINUX_MODE_DICT.get(configmode, 'unknown')
  395. else:
  396. self.facts['selinux']['config_mode'] = 'unknown'
  397. except OSError, e:
  398. self.facts['selinux']['config_mode'] = 'unknown'
  399. try:
  400. mode = selinux.security_getenforce()
  401. self.facts['selinux']['mode'] = Facts.SELINUX_MODE_DICT.get(mode, 'unknown')
  402. except OSError, e:
  403. self.facts['selinux']['mode'] = 'unknown'
  404. try:
  405. (rc, policytype) = selinux.selinux_getpolicytype()
  406. if rc == 0:
  407. self.facts['selinux']['type'] = policytype
  408. else:
  409. self.facts['selinux']['type'] = 'unknown'
  410. except OSError, e:
  411. self.facts['selinux']['type'] = 'unknown'
  412. def get_date_time_facts(self):
  413. self.facts['date_time'] = {}
  414. now = datetime.datetime.now()
  415. self.facts['date_time']['year'] = now.strftime('%Y')
  416. self.facts['date_time']['month'] = now.strftime('%m')
  417. self.facts['date_time']['weekday'] = now.strftime('%A')
  418. self.facts['date_time']['day'] = now.strftime('%d')
  419. self.facts['date_time']['hour'] = now.strftime('%H')
  420. self.facts['date_time']['minute'] = now.strftime('%M')
  421. self.facts['date_time']['second'] = now.strftime('%S')
  422. self.facts['date_time']['epoch'] = now.strftime('%s')
  423. if self.facts['date_time']['epoch'] == '' or self.facts['date_time']['epoch'][0] == '%':
  424. self.facts['date_time']['epoch'] = str(int(time.time()))
  425. self.facts['date_time']['date'] = now.strftime('%Y-%m-%d')
  426. self.facts['date_time']['time'] = now.strftime('%H:%M:%S')
  427. self.facts['date_time']['iso8601_micro'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
  428. self.facts['date_time']['iso8601'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
  429. self.facts['date_time']['tz'] = time.strftime("%Z")
  430. self.facts['date_time']['tz_offset'] = time.strftime("%z")
  431. # User
  432. def get_user_facts(self):
  433. self.facts['user_id'] = getpass.getuser()
  434. def get_env_facts(self):
  435. self.facts['env'] = {}
  436. for k,v in os.environ.iteritems():
  437. self.facts['env'][k] = v
  438. class Hardware(Facts):
  439. """
  440. This is a generic Hardware subclass of Facts. This should be further
  441. subclassed to implement per platform. If you subclass this, it
  442. should define:
  443. - memfree_mb
  444. - memtotal_mb
  445. - swapfree_mb
  446. - swaptotal_mb
  447. - processor (a list)
  448. - processor_cores
  449. - processor_count
  450. All subclasses MUST define platform.
  451. """
  452. platform = 'Generic'
  453. def __new__(cls, *arguments, **keyword):
  454. subclass = cls
  455. for sc in Hardware.__subclasses__():
  456. if sc.platform == platform.system():
  457. subclass = sc
  458. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  459. def __init__(self):
  460. Facts.__init__(self)
  461. def populate(self):
  462. return self.facts
  463. class LinuxHardware(Hardware):
  464. """
  465. Linux-specific subclass of Hardware. Defines memory and CPU facts:
  466. - memfree_mb
  467. - memtotal_mb
  468. - swapfree_mb
  469. - swaptotal_mb
  470. - processor (a list)
  471. - processor_cores
  472. - processor_count
  473. In addition, it also defines number of DMI facts and device facts.
  474. """
  475. platform = 'Linux'
  476. MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
  477. def __init__(self):
  478. Hardware.__init__(self)
  479. def populate(self):
  480. self.get_cpu_facts()
  481. self.get_memory_facts()
  482. self.get_dmi_facts()
  483. self.get_device_facts()
  484. try:
  485. self.get_mount_facts()
  486. except TimeoutError:
  487. pass
  488. return self.facts
  489. def get_memory_facts(self):
  490. if not os.access("/proc/meminfo", os.R_OK):
  491. return
  492. for line in open("/proc/meminfo").readlines():
  493. data = line.split(":", 1)
  494. key = data[0]
  495. if key in LinuxHardware.MEMORY_FACTS:
  496. val = data[1].strip().split(' ')[0]
  497. self.facts["%s_mb" % key.lower()] = long(val) / 1024
  498. def get_cpu_facts(self):
  499. i = 0
  500. physid = 0
  501. coreid = 0
  502. sockets = {}
  503. cores = {}
  504. if not os.access("/proc/cpuinfo", os.R_OK):
  505. return
  506. self.facts['processor'] = []
  507. for line in open("/proc/cpuinfo").readlines():
  508. data = line.split(":", 1)
  509. key = data[0].strip()
  510. # model name is for Intel arch, Processor (mind the uppercase P)
  511. # works for some ARM devices, like the Sheevaplug.
  512. if key == 'model name' or key == 'Processor':
  513. if 'processor' not in self.facts:
  514. self.facts['processor'] = []
  515. self.facts['processor'].append(data[1].strip())
  516. i += 1
  517. elif key == 'physical id':
  518. physid = data[1].strip()
  519. if physid not in sockets:
  520. sockets[physid] = 1
  521. elif key == 'core id':
  522. coreid = data[1].strip()
  523. if coreid not in sockets:
  524. cores[coreid] = 1
  525. elif key == 'cpu cores':
  526. sockets[physid] = int(data[1].strip())
  527. elif key == 'siblings':
  528. cores[coreid] = int(data[1].strip())
  529. self.facts['processor_count'] = sockets and len(sockets) or i
  530. self.facts['processor_cores'] = sockets.values() and sockets.values()[0] or 1
  531. self.facts['processor_threads_per_core'] = ((cores.values() and
  532. cores.values()[0] or 1) / self.facts['processor_cores'])
  533. self.facts['processor_vcpus'] = (self.facts['processor_threads_per_core'] *
  534. self.facts['processor_count'] * self.facts['processor_cores'])
  535. def get_dmi_facts(self):
  536. ''' learn dmi facts from system
  537. Try /sys first for dmi related facts.
  538. If that is not available, fall back to dmidecode executable '''
  539. if os.path.exists('/sys/devices/virtual/dmi/id/product_name'):
  540. # Use kernel DMI info, if available
  541. # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
  542. FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
  543. "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
  544. "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
  545. "All In One", "Sub Notebook", "Space-saving", "Lunch Box",
  546. "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
  547. "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
  548. "Rack Mount Chassis", "Sealed-case PC", "Multi-system",
  549. "CompactPCI", "AdvancedTCA", "Blade" ]
  550. DMI_DICT = {
  551. 'bios_date': '/sys/devices/virtual/dmi/id/bios_date',
  552. 'bios_version': '/sys/devices/virtual/dmi/id/bios_version',
  553. 'form_factor': '/sys/devices/virtual/dmi/id/chassis_type',
  554. 'product_name': '/sys/devices/virtual/dmi/id/product_name',
  555. 'product_serial': '/sys/devices/virtual/dmi/id/product_serial',
  556. 'product_uuid': '/sys/devices/virtual/dmi/id/product_uuid',
  557. 'product_version': '/sys/devices/virtual/dmi/id/product_version',
  558. 'system_vendor': '/sys/devices/virtual/dmi/id/sys_vendor'
  559. }
  560. for (key,path) in DMI_DICT.items():
  561. data = get_file_content(path)
  562. if data is not None:
  563. if key == 'form_factor':
  564. try:
  565. self.facts['form_factor'] = FORM_FACTOR[int(data)]
  566. except IndexError, e:
  567. self.facts['form_factor'] = 'unknown (%s)' % data
  568. else:
  569. self.facts[key] = data
  570. else:
  571. self.facts[key] = 'NA'
  572. else:
  573. # Fall back to using dmidecode, if available
  574. dmi_bin = module.get_bin_path('dmidecode')
  575. DMI_DICT = {
  576. 'bios_date': 'bios-release-date',
  577. 'bios_version': 'bios-version',
  578. 'form_factor': 'chassis-type',
  579. 'product_name': 'system-product-name',
  580. 'product_serial': 'system-serial-number',
  581. 'product_uuid': 'system-uuid',
  582. 'product_version': 'system-version',
  583. 'system_vendor': 'system-manufacturer'
  584. }
  585. for (k, v) in DMI_DICT.items():
  586. if dmi_bin is not None:
  587. (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
  588. if rc == 0:
  589. # Strip out commented lines (specific dmidecode output)
  590. thisvalue = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
  591. try:
  592. json.dumps(thisvalue)
  593. except UnicodeDecodeError:
  594. thisvalue = "NA"
  595. self.facts[k] = thisvalue
  596. else:
  597. self.facts[k] = 'NA'
  598. else:
  599. self.facts[k] = 'NA'
  600. @timeout(10)
  601. def get_mount_facts(self):
  602. self.facts['mounts'] = []
  603. mtab = get_file_content('/etc/mtab', '')
  604. for line in mtab.split('\n'):
  605. if line.startswith('/'):
  606. fields = line.rstrip('\n').split()
  607. if(fields[2] != 'none'):
  608. size_total = None
  609. size_available = None
  610. try:
  611. statvfs_result = os.statvfs(fields[1])
  612. size_total = statvfs_result.f_bsize * statvfs_result.f_blocks
  613. size_available = statvfs_result.f_bsize * (statvfs_result.f_bavail)
  614. except OSError, e:
  615. continue
  616. self.facts['mounts'].append(
  617. {'mount': fields[1],
  618. 'device':fields[0],
  619. 'fstype': fields[2],
  620. 'options': fields[3],
  621. # statvfs data
  622. 'size_total': size_total,
  623. 'size_available': size_available,
  624. })
  625. def get_device_facts(self):
  626. self.facts['devices'] = {}
  627. lspci = module.get_bin_path('lspci')
  628. if lspci:
  629. rc, pcidata, err = module.run_command([lspci, '-D'])
  630. else:
  631. pcidata = None
  632. try:
  633. block_devs = os.listdir("/sys/block")
  634. except OSError:
  635. return
  636. for block in block_devs:
  637. virtual = 1
  638. sysfs_no_links = 0
  639. try:
  640. path = os.readlink(os.path.join("/sys/block/", block))
  641. except OSError, e:
  642. if e.errno == errno.EINVAL:
  643. path = block
  644. sysfs_no_links = 1
  645. else:
  646. continue
  647. if "virtual" in path:
  648. continue
  649. sysdir = os.path.join("/sys/block", path)
  650. if sysfs_no_links == 1:
  651. for folder in os.listdir(sysdir):
  652. if "device" in folder:
  653. virtual = 0
  654. break
  655. if virtual:
  656. continue
  657. d = {}
  658. diskname = os.path.basename(sysdir)
  659. for key in ['vendor', 'model']:
  660. d[key] = get_file_content(sysdir + "/device/" + key)
  661. for key,test in [ ('removable','/removable'), \
  662. ('support_discard','/queue/discard_granularity'),
  663. ]:
  664. d[key] = get_file_content(sysdir + test)
  665. d['partitions'] = {}
  666. for folder in os.listdir(sysdir):
  667. m = re.search("(" + diskname + "\d+)", folder)
  668. if m:
  669. part = {}
  670. partname = m.group(1)
  671. part_sysdir = sysdir + "/" + partname
  672. part['start'] = get_file_content(part_sysdir + "/start",0)
  673. part['sectors'] = get_file_content(part_sysdir + "/size",0)
  674. part['sectorsize'] = get_file_content(part_sysdir + "/queue/physical_block_size")
  675. if not part['sectorsize']:
  676. part['sectorsize'] = get_file_content(part_sysdir + "/queue/hw_sector_size",512)
  677. part['size'] = module.pretty_bytes((float(part['sectors']) * float(part['sectorsize'])))
  678. d['partitions'][partname] = part
  679. d['rotational'] = get_file_content(sysdir + "/queue/rotational")
  680. d['scheduler_mode'] = ""
  681. scheduler = get_file_content(sysdir + "/queue/scheduler")
  682. if scheduler is not None:
  683. m = re.match(".*?(\[(.*)\])", scheduler)
  684. if m:
  685. d['scheduler_mode'] = m.group(2)
  686. d['sectors'] = get_file_content(sysdir + "/size")
  687. if not d['sectors']:
  688. d['sectors'] = 0
  689. d['sectorsize'] = get_file_content(sysdir + "/queue/physical_block_size")
  690. if not d['sectorsize']:
  691. d['sectorsize'] = get_file_content(sysdir + "/queue/hw_sector_size",512)
  692. d['size'] = module.pretty_bytes(float(d['sectors']) * float(d['sectorsize']))
  693. d['host'] = ""
  694. # domains are numbered (0 to ffff), bus (0 to ff), slot (0 to 1f), and function (0 to 7).
  695. m = re.match(".+/([a-f0-9]{4}:[a-f0-9]{2}:[0|1][a-f0-9]\.[0-7])/", sysdir)
  696. if m and pcidata:
  697. pciid = m.group(1)
  698. did = re.escape(pciid)
  699. m = re.search("^" + did + "\s(.*)$", pcidata, re.MULTILINE)
  700. d['host'] = m.group(1)
  701. d['holders'] = []
  702. if os.path.isdir(sysdir + "/holders"):
  703. for folder in os.listdir(sysdir + "/holders"):
  704. if not folder.startswith("dm-"):
  705. continue
  706. name = get_file_content(sysdir + "/holders/" + folder + "/dm/name")
  707. if name:
  708. d['holders'].append(name)
  709. else:
  710. d['holders'].append(folder)
  711. self.facts['devices'][diskname] = d
  712. class SunOSHardware(Hardware):
  713. """
  714. In addition to the generic memory and cpu facts, this also sets
  715. swap_reserved_mb and swap_allocated_mb that is available from *swap -s*.
  716. """
  717. platform = 'SunOS'
  718. def __init__(self):
  719. Hardware.__init__(self)
  720. def populate(self):
  721. self.get_cpu_facts()
  722. self.get_memory_facts()
  723. return self.facts
  724. def get_cpu_facts(self):
  725. physid = 0
  726. sockets = {}
  727. rc, out, err = module.run_command("/usr/bin/kstat cpu_info")
  728. self.facts['processor'] = []
  729. for line in out.split('\n'):
  730. if len(line) < 1:
  731. continue
  732. data = line.split(None, 1)
  733. key = data[0].strip()
  734. # "brand" works on Solaris 10 & 11. "implementation" for Solaris 9.
  735. if key == 'module:':
  736. brand = ''
  737. elif key == 'brand':
  738. brand = data[1].strip()
  739. elif key == 'clock_MHz':
  740. clock_mhz = data[1].strip()
  741. elif key == 'implementation':
  742. processor = brand or data[1].strip()
  743. # Add clock speed to description for SPARC CPU
  744. if self.facts['machine'] != 'i86pc':
  745. processor += " @ " + clock_mhz + "MHz"
  746. if 'processor' not in self.facts:
  747. self.facts['processor'] = []
  748. self.facts['processor'].append(processor)
  749. elif key == 'chip_id':
  750. physid = data[1].strip()
  751. if physid not in sockets:
  752. sockets[physid] = 1
  753. else:
  754. sockets[physid] += 1
  755. # Counting cores on Solaris can be complicated.
  756. # https://blogs.oracle.com/mandalika/entry/solaris_show_me_the_cpu
  757. # Treat 'processor_count' as physical sockets and 'processor_cores' as
  758. # virtual CPUs visisble to Solaris. Not a true count of cores for modern SPARC as
  759. # these processors have: sockets -> cores -> threads/virtual CPU.
  760. if len(sockets) > 0:
  761. self.facts['processor_count'] = len(sockets)
  762. self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
  763. else:
  764. self.facts['processor_cores'] = 'NA'
  765. self.facts['processor_count'] = len(self.facts['processor'])
  766. def get_memory_facts(self):
  767. rc, out, err = module.run_command(["/usr/sbin/prtconf"])
  768. for line in out.split('\n'):
  769. if 'Memory size' in line:
  770. self.facts['memtotal_mb'] = line.split()[2]
  771. rc, out, err = module.run_command("/usr/sbin/swap -s")
  772. allocated = long(out.split()[1][:-1])
  773. reserved = long(out.split()[5][:-1])
  774. used = long(out.split()[8][:-1])
  775. free = long(out.split()[10][:-1])
  776. self.facts['swapfree_mb'] = free / 1024
  777. self.facts['swaptotal_mb'] = (free + used) / 1024
  778. self.facts['swap_allocated_mb'] = allocated / 1024
  779. self.facts['swap_reserved_mb'] = reserved / 1024
  780. class OpenBSDHardware(Hardware):
  781. """
  782. OpenBSD-specific subclass of Hardware. Defines memory, CPU and device facts:
  783. - memfree_mb
  784. - memtotal_mb
  785. - swapfree_mb
  786. - swaptotal_mb
  787. - processor (a list)
  788. - processor_cores
  789. - processor_count
  790. - processor_speed
  791. - devices
  792. """
  793. platform = 'OpenBSD'
  794. DMESG_BOOT = '/var/run/dmesg.boot'
  795. def __init__(self):
  796. Hardware.__init__(self)
  797. def populate(self):
  798. self.sysctl = self.get_sysctl()
  799. self.get_memory_facts()
  800. self.get_processor_facts()
  801. self.get_device_facts()
  802. return self.facts
  803. def get_sysctl(self):
  804. rc, out, err = module.run_command(["/sbin/sysctl", "hw"])
  805. if rc != 0:
  806. return dict()
  807. sysctl = dict()
  808. for line in out.splitlines():
  809. (key, value) = line.split('=')
  810. sysctl[key] = value.strip()
  811. return sysctl
  812. def get_memory_facts(self):
  813. # Get free memory. vmstat output looks like:
  814. # procs memory page disks traps cpu
  815. # r b w avm fre flt re pi po fr sr wd0 fd0 int sys cs us sy id
  816. # 0 0 0 47512 28160 51 0 0 0 0 0 1 0 116 89 17 0 1 99
  817. rc, out, err = module.run_command("/usr/bin/vmstat")
  818. if rc == 0:
  819. self.facts['memfree_mb'] = long(out.splitlines()[-1].split()[4]) / 1024
  820. self.facts['memtotal_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024
  821. # Get swapctl info. swapctl output looks like:
  822. # total: 69268 1K-blocks allocated, 0 used, 69268 available
  823. # And for older OpenBSD:
  824. # total: 69268k bytes allocated = 0k used, 69268k available
  825. rc, out, err = module.run_command("/sbin/swapctl -sk")
  826. if rc == 0:
  827. data = out.split()
  828. self.facts['swapfree_mb'] = long(data[-2].translate(None, "kmg")) / 1024
  829. self.facts['swaptotal_mb'] = long(data[1].translate(None, "kmg")) / 1024
  830. def get_processor_facts(self):
  831. processor = []
  832. dmesg_boot = get_file_content(OpenBSDHardware.DMESG_BOOT)
  833. if not dmesg_boot:
  834. rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
  835. i = 0
  836. for line in dmesg_boot.splitlines():
  837. if line.split(' ', 1)[0] == 'cpu%i:' % i:
  838. processor.append(line.split(' ', 1)[1])
  839. i = i + 1
  840. processor_count = i
  841. self.facts['processor'] = processor
  842. self.facts['processor_count'] = processor_count
  843. # I found no way to figure out the number of Cores per CPU in OpenBSD
  844. self.facts['processor_cores'] = 'NA'
  845. def get_device_facts(self):
  846. devices = []
  847. devices.extend(self.sysctl['hw.disknames'].split(','))
  848. self.facts['devices'] = devices
  849. class FreeBSDHardware(Hardware):
  850. """
  851. FreeBSD-specific subclass of Hardware. Defines memory and CPU facts:
  852. - memfree_mb
  853. - memtotal_mb
  854. - swapfree_mb
  855. - swaptotal_mb
  856. - processor (a list)
  857. - processor_cores
  858. - processor_count
  859. - devices
  860. """
  861. platform = 'FreeBSD'
  862. DMESG_BOOT = '/var/run/dmesg.boot'
  863. def __init__(self):
  864. Hardware.__init__(self)
  865. def populate(self):
  866. self.get_cpu_facts()
  867. self.get_memory_facts()
  868. self.get_dmi_facts()
  869. self.get_device_facts()
  870. try:
  871. self.get_mount_facts()
  872. except TimeoutError:
  873. pass
  874. return self.facts
  875. def get_cpu_facts(self):
  876. self.facts['processor'] = []
  877. rc, out, err = module.run_command("/sbin/sysctl -n hw.ncpu")
  878. self.facts['processor_count'] = out.strip()
  879. dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT)
  880. if not dmesg_boot:
  881. rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
  882. for line in dmesg_boot.split('\n'):
  883. if 'CPU:' in line:
  884. cpu = re.sub(r'CPU:\s+', r"", line)
  885. self.facts['processor'].append(cpu.strip())
  886. if 'Logical CPUs per core' in line:
  887. self.facts['processor_cores'] = line.split()[4]
  888. def get_memory_facts(self):
  889. rc, out, err = module.run_command("/sbin/sysctl vm.stats")
  890. for line in out.split('\n'):
  891. data = line.split()
  892. if 'vm.stats.vm.v_page_size' in line:
  893. pagesize = long(data[1])
  894. if 'vm.stats.vm.v_page_count' in line:
  895. pagecount = long(data[1])
  896. if 'vm.stats.vm.v_free_count' in line:
  897. freecount = long(data[1])
  898. self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
  899. self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
  900. # Get swapinfo. swapinfo output looks like:
  901. # Device 1M-blocks Used Avail Capacity
  902. # /dev/ada0p3 314368 0 314368 0%
  903. #
  904. rc, out, err = module.run_command("/usr/sbin/swapinfo -m")
  905. lines = out.split('\n')
  906. if len(lines[-1]) == 0:
  907. lines.pop()
  908. data = lines[-1].split()
  909. self.facts['swaptotal_mb'] = data[1]
  910. self.facts['swapfree_mb'] = data[3]
  911. @timeout(10)
  912. def get_mount_facts(self):
  913. self.facts['mounts'] = []
  914. fstab = get_file_content('/etc/fstab')
  915. if fstab:
  916. for line in fstab.split('\n'):
  917. if line.startswith('#') or line.strip() == '':
  918. continue
  919. fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
  920. self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
  921. def get_device_facts(self):
  922. sysdir = '/dev'
  923. self.facts['devices'] = {}
  924. drives = re.compile('(ada?\d+|da\d+|a?cd\d+)') #TODO: rc, disks, err = module.run_command("/sbin/sysctl kern.disks")
  925. slices = re.compile('(ada?\d+s\d+\w*|da\d+s\d+\w*)')
  926. if os.path.isdir(sysdir):
  927. dirlist = sorted(os.listdir(sysdir))
  928. for device in dirlist:
  929. d = drives.match(device)
  930. if d:
  931. self.facts['devices'][d.group(1)] = []
  932. s = slices.match(device)
  933. if s:
  934. self.facts['devices'][d.group(1)].append(s.group(1))
  935. def get_dmi_facts(self):
  936. ''' learn dmi facts from system
  937. Use dmidecode executable if available'''
  938. # Fall back to using dmidecode, if available
  939. dmi_bin = module.get_bin_path('dmidecode')
  940. DMI_DICT = dict(
  941. bios_date='bios-release-date',
  942. bios_version='bios-version',
  943. form_factor='chassis-type',
  944. product_name='system-product-name',
  945. product_serial='system-serial-number',
  946. product_uuid='system-uuid',
  947. product_version='system-version',
  948. system_vendor='system-manufacturer'
  949. )
  950. for (k, v) in DMI_DICT.items():
  951. if dmi_bin is not None:
  952. (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
  953. if rc == 0:
  954. # Strip out commented lines (specific dmidecode output)
  955. self.facts[k] = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
  956. try:
  957. json.dumps(self.facts[k])
  958. except UnicodeDecodeError:
  959. self.facts[k] = 'NA'
  960. else:
  961. self.facts[k] = 'NA'
  962. else:
  963. self.facts[k] = 'NA'
  964. class NetBSDHardware(Hardware):
  965. """
  966. NetBSD-specific subclass of Hardware. Defines memory and CPU facts:
  967. - memfree_mb
  968. - memtotal_mb
  969. - swapfree_mb
  970. - swaptotal_mb
  971. - processor (a list)
  972. - processor_cores
  973. - processor_count
  974. - devices
  975. """
  976. platform = 'NetBSD'
  977. MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
  978. def __init__(self):
  979. Hardware.__init__(self)
  980. def populate(self):
  981. self.get_cpu_facts()
  982. self.get_memory_facts()
  983. try:
  984. self.get_mount_facts()
  985. except TimeoutError:
  986. pass
  987. return self.facts
  988. def get_cpu_facts(self):
  989. i = 0
  990. physid = 0
  991. sockets = {}
  992. if not os.access("/proc/cpuinfo", os.R_OK):
  993. return
  994. self.facts['processor'] = []
  995. for line in open("/proc/cpuinfo").readlines():
  996. data = line.split(":", 1)
  997. key = data[0].strip()
  998. # model name is for Intel arch, Processor (mind the uppercase P)
  999. # works for some ARM devices, like the Sheevaplug.
  1000. if key == 'model name' or key == 'Processor':
  1001. if 'processor' not in self.facts:
  1002. self.facts['processor'] = []
  1003. self.facts['processor'].append(data[1].strip())
  1004. i += 1
  1005. elif key == 'physical id':
  1006. physid = data[1].strip()
  1007. if physid not in sockets:
  1008. sockets[physid] = 1
  1009. elif key == 'cpu cores':
  1010. sockets[physid] = int(data[1].strip())
  1011. if len(sockets) > 0:
  1012. self.facts['processor_count'] = len(sockets)
  1013. self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
  1014. else:
  1015. self.facts['processor_count'] = i
  1016. self.facts['processor_cores'] = 'NA'
  1017. def get_memory_facts(self):
  1018. if not os.access("/proc/meminfo", os.R_OK):
  1019. return
  1020. for line in open("/proc/meminfo").readlines():
  1021. data = line.split(":", 1)
  1022. key = data[0]
  1023. if key in NetBSDHardware.MEMORY_FACTS:
  1024. val = data[1].strip().split(' ')[0]
  1025. self.facts["%s_mb" % key.lower()] = long(val) / 1024
  1026. @timeout(10)
  1027. def get_mount_facts(self):
  1028. self.facts['mounts'] = []
  1029. fstab = get_file_content('/etc/fstab')
  1030. if fstab:
  1031. for line in fstab.split('\n'):
  1032. if line.startswith('#') or line.strip() == '':
  1033. continue
  1034. fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
  1035. self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
  1036. class AIX(Hardware):
  1037. """
  1038. AIX-specific subclass of Hardware. Defines memory and CPU facts:
  1039. - memfree_mb
  1040. - memtotal_mb
  1041. - swapfree_mb
  1042. - swaptotal_mb
  1043. - processor (a list)
  1044. - processor_cores
  1045. - processor_count
  1046. """
  1047. platform = 'AIX'
  1048. def __init__(self):
  1049. Hardware.__init__(self)
  1050. def populate(self):
  1051. self.get_cpu_facts()
  1052. self.get_memory_facts()
  1053. self.get_dmi_facts()
  1054. return self.facts
  1055. def get_cpu_facts(self):
  1056. self.facts['processor'] = []
  1057. rc, out, err = module.run_command("/usr/sbin/lsdev -Cc processor")
  1058. if out:
  1059. i = 0
  1060. for line in out.split('\n'):
  1061. if 'Available' in line:
  1062. if i == 0:
  1063. data = line.split(' ')
  1064. cpudev = data[0]
  1065. i += 1
  1066. self.facts['processor_count'] = int(i)
  1067. rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a type")
  1068. data = out.split(' ')
  1069. self.facts['processor'] = data[1]
  1070. rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a smt_threads")
  1071. data = out.split(' ')
  1072. self.facts['processor_cores'] = int(data[1])
  1073. def get_memory_facts(self):
  1074. pagesize = 4096
  1075. rc, out, err = module.run_command("/usr/bin/vmstat -v")
  1076. for line in out.split('\n'):
  1077. data = line.split()
  1078. if 'memory pages' in line:
  1079. pagecount = long(data[0])
  1080. if 'free pages' in line:
  1081. freecount = long(data[0])
  1082. self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
  1083. self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
  1084. # Get swapinfo. swapinfo output looks like:
  1085. # Device 1M-blocks Used Avail Capacity
  1086. # /dev/ada0p3 314368 0 314368 0%
  1087. #
  1088. rc, out, err = module.run_command("/usr/sbin/lsps -s")
  1089. if out:
  1090. lines = out.split('\n')
  1091. data = lines[1].split()
  1092. swaptotal_mb = long(data[0].rstrip('MB'))
  1093. percused = int(data[1].rstrip('%'))
  1094. self.facts['swaptotal_mb'] = swaptotal_mb
  1095. self.facts['swapfree_mb'] = long(swaptotal_mb * ( 100 - percused ) / 100)
  1096. def get_dmi_facts(self):
  1097. rc, out, err = module.run_command("/usr/sbin/lsattr -El sys0 -a fwversion")
  1098. data = out.split()
  1099. self.facts['firmware_version'] = data[1].strip('IBM,')
  1100. class HPUX(Hardware):
  1101. """
  1102. HP-UX-specifig subclass of Hardware. Defines memory and CPU facts:
  1103. - memfree_mb
  1104. - memtotal_mb
  1105. - swapfree_mb
  1106. - swaptotal_mb
  1107. - processor
  1108. - processor_cores
  1109. - processor_count
  1110. - model
  1111. - firmware
  1112. """
  1113. platform = 'HP-UX'
  1114. def __init__(self):
  1115. Hardware.__init__(self)
  1116. def populate(self):
  1117. self.get_cpu_facts()
  1118. self.get_memory_facts()
  1119. self.get_hw_facts()
  1120. return self.facts
  1121. def get_cpu_facts(self):