PageRenderTime 79ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ansible/module_utils/facts.py

https://github.com/ajanthanm/ansible
Python | 2371 lines | 2067 code | 133 blank | 171 comment | 227 complexity | a9c77d38aded62bddd620d444873d9fa MD5 | raw file
Possible License(s): GPL-3.0
  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):
  1122. if self.facts['architecture'] == '9000/800':
  1123. rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True)
  1124. self.facts['processor_count'] = int(out.strip())
  1125. #Working with machinfo mess
  1126. elif self.facts['architecture'] == 'ia64':
  1127. if self.facts['distribution_version'] == "B.11.23":
  1128. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'Number of CPUs'", use_unsafe_shell=True)
  1129. self.facts['processor_count'] = int(out.strip().split('=')[1])
  1130. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'processor family'", use_unsafe_shell=True)
  1131. self.facts['processor'] = re.search('.*(Intel.*)', out).groups()[0].strip()
  1132. rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True)
  1133. self.facts['processor_cores'] = int(out.strip())
  1134. if self.facts['distribution_version'] == "B.11.31":
  1135. #if machinfo return cores strings release B.11.31 > 1204
  1136. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep core | wc -l", use_unsafe_shell=True)
  1137. if out.strip()== '0':
  1138. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True)
  1139. self.facts['processor_count'] = int(out.strip().split(" ")[0])
  1140. #If hyperthreading is active divide cores by 2
  1141. rc, out, err = module.run_command("/usr/sbin/psrset | grep LCPU", use_unsafe_shell=True)
  1142. data = re.sub(' +',' ',out).strip().split(' ')
  1143. if len(data) == 1:
  1144. hyperthreading = 'OFF'
  1145. else:
  1146. hyperthreading = data[1]
  1147. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep logical", use_unsafe_shell=True)
  1148. data = out.strip().split(" ")
  1149. if hyperthreading == 'ON':
  1150. self.facts['processor_cores'] = int(data[0])/2
  1151. else:
  1152. if len(data) == 1:
  1153. self.facts['processor_cores'] = self.facts['processor_count']
  1154. else:
  1155. self.facts['processor_cores'] = int(data[0])
  1156. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel |cut -d' ' -f4-", use_unsafe_shell=True)
  1157. self.facts['processor'] = out.strip()
  1158. else:
  1159. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | egrep 'socket[s]?$' | tail -1", use_unsafe_shell=True)
  1160. self.facts['processor_count'] = int(out.strip().split(" ")[0])
  1161. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep -e '[0-9] core' | tail -1", use_unsafe_shell=True)
  1162. self.facts['processor_cores'] = int(out.strip().split(" ")[0])
  1163. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True)
  1164. self.facts['processor'] = out.strip()
  1165. def get_memory_facts(self):
  1166. pagesize = 4096
  1167. rc, out, err = module.run_command("/usr/bin/vmstat | tail -1", use_unsafe_shell=True)
  1168. data = int(re.sub(' +',' ',out).split(' ')[5].strip())
  1169. self.facts['memfree_mb'] = pagesize * data / 1024 / 1024
  1170. if self.facts['architecture'] == '9000/800':
  1171. try:
  1172. rc, out, err = module.run_command("grep Physical /var/adm/syslog/syslog.log")
  1173. data = re.search('.*Physical: ([0-9]*) Kbytes.*',out).groups()[0].strip()
  1174. self.facts['memtotal_mb'] = int(data) / 1024
  1175. except AttributeError:
  1176. #For systems where memory details aren't sent to syslog or the log has rotated, use parsed
  1177. #adb output. Unfortunatley /dev/kmem doesn't have world-read, so this only works as root.
  1178. if os.access("/dev/kmem", os.R_OK):
  1179. rc, out, err = module.run_command("echo 'phys_mem_pages/D' | adb -k /stand/vmunix /dev/kmem | tail -1 | awk '{print $2}'", use_unsafe_shell=True)
  1180. if not err:
  1181. data = out
  1182. self.facts['memtotal_mb'] = int(data) / 256
  1183. else:
  1184. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Memory", use_unsafe_shell=True)
  1185. data = re.search('Memory[\ :=]*([0-9]*).*MB.*',out).groups()[0].strip()
  1186. self.facts['memtotal_mb'] = int(data)
  1187. rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f -q")
  1188. self.facts['swaptotal_mb'] = int(out.strip())
  1189. rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f | egrep '^dev|^fs'", use_unsafe_shell=True)
  1190. swap = 0
  1191. for line in out.strip().split('\n'):
  1192. swap += int(re.sub(' +',' ',line).split(' ')[3].strip())
  1193. self.facts['swapfree_mb'] = swap
  1194. def get_hw_facts(self):
  1195. rc, out, err = module.run_command("model")
  1196. self.facts['model'] = out.strip()
  1197. if self.facts['architecture'] == 'ia64':
  1198. separator = ':'
  1199. if self.facts['distribution_version'] == "B.11.23":
  1200. separator = '='
  1201. rc, out, err = module.run_command("/usr/contrib/bin/machinfo |grep -i 'Firmware revision' | grep -v BMC", use_unsafe_shell=True)
  1202. self.facts['firmware_version'] = out.split(separator)[1].strip()
  1203. class Darwin(Hardware):
  1204. """
  1205. Darwin-specific subclass of Hardware. Defines memory and CPU facts:
  1206. - processor
  1207. - processor_cores
  1208. - memtotal_mb
  1209. - memfree_mb
  1210. - model
  1211. - osversion
  1212. - osrevision
  1213. """
  1214. platform = 'Darwin'
  1215. def __init__(self):
  1216. Hardware.__init__(self)
  1217. def populate(self):
  1218. self.sysctl = self.get_sysctl()
  1219. self.get_mac_facts()
  1220. self.get_cpu_facts()
  1221. self.get_memory_facts()
  1222. return self.facts
  1223. def get_sysctl(self):
  1224. rc, out, err = module.run_command(["/usr/sbin/sysctl", "hw", "machdep", "kern"])
  1225. if rc != 0:
  1226. return dict()
  1227. sysctl = dict()
  1228. for line in out.splitlines():
  1229. if line.rstrip("\n"):
  1230. (key, value) = re.split(' = |: ', line, maxsplit=1)
  1231. sysctl[key] = value.strip()
  1232. return sysctl
  1233. def get_system_profile(self):
  1234. rc, out, err = module.run_command(["/usr/sbin/system_profiler", "SPHardwareDataType"])
  1235. if rc != 0:
  1236. return dict()
  1237. system_profile = dict()
  1238. for line in out.splitlines():
  1239. if ': ' in line:
  1240. (key, value) = line.split(': ', 1)
  1241. system_profile[key.strip()] = ' '.join(value.strip().split())
  1242. return system_profile
  1243. def get_mac_facts(self):
  1244. self.facts['model'] = self.sysctl['hw.model']
  1245. self.facts['osversion'] = self.sysctl['kern.osversion']
  1246. self.facts['osrevision'] = self.sysctl['kern.osrevision']
  1247. def get_cpu_facts(self):
  1248. if 'machdep.cpu.brand_string' in self.sysctl: # Intel
  1249. self.facts['processor'] = self.sysctl['machdep.cpu.brand_string']
  1250. self.facts['processor_cores'] = self.sysctl['machdep.cpu.core_count']
  1251. else: # PowerPC
  1252. system_profile = self.get_system_profile()
  1253. self.facts['processor'] = '%s @ %s' % (system_profile['Processor Name'], system_profile['Processor Speed'])
  1254. self.facts['processor_cores'] = self.sysctl['hw.physicalcpu']
  1255. def get_memory_facts(self):
  1256. self.facts['memtotal_mb'] = long(self.sysctl['hw.memsize']) / 1024 / 1024
  1257. self.facts['memfree_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024
  1258. class Network(Facts):
  1259. """
  1260. This is a generic Network subclass of Facts. This should be further
  1261. subclassed to implement per platform. If you subclass this,
  1262. you must define:
  1263. - interfaces (a list of interface names)
  1264. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1265. All subclasses MUST define platform.
  1266. """
  1267. platform = 'Generic'
  1268. IPV6_SCOPE = { '0' : 'global',
  1269. '10' : 'host',
  1270. '20' : 'link',
  1271. '40' : 'admin',
  1272. '50' : 'site',
  1273. '80' : 'organization' }
  1274. def __new__(cls, *arguments, **keyword):
  1275. subclass = cls
  1276. for sc in Network.__subclasses__():
  1277. if sc.platform == platform.system():
  1278. subclass = sc
  1279. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  1280. def __init__(self, module):
  1281. self.module = module
  1282. Facts.__init__(self)
  1283. def populate(self):
  1284. return self.facts
  1285. class LinuxNetwork(Network):
  1286. """
  1287. This is a Linux-specific subclass of Network. It defines
  1288. - interfaces (a list of interface names)
  1289. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1290. - all_ipv4_addresses and all_ipv6_addresses: lists of all configured addresses.
  1291. - ipv4_address and ipv6_address: the first non-local address for each family.
  1292. """
  1293. platform = 'Linux'
  1294. def __init__(self, module):
  1295. Network.__init__(self, module)
  1296. def populate(self):
  1297. ip_path = self.module.get_bin_path('ip')
  1298. if ip_path is None:
  1299. return self.facts
  1300. default_ipv4, default_ipv6 = self.get_default_interfaces(ip_path)
  1301. interfaces, ips = self.get_interfaces_info(ip_path, default_ipv4, default_ipv6)
  1302. self.facts['interfaces'] = interfaces.keys()
  1303. for iface in interfaces:
  1304. self.facts[iface] = interfaces[iface]
  1305. self.facts['default_ipv4'] = default_ipv4
  1306. self.facts['default_ipv6'] = default_ipv6
  1307. self.facts['all_ipv4_addresses'] = ips['all_ipv4_addresses']
  1308. self.facts['all_ipv6_addresses'] = ips['all_ipv6_addresses']
  1309. return self.facts
  1310. def get_default_interfaces(self, ip_path):
  1311. # Use the commands:
  1312. # ip -4 route get 8.8.8.8 -> Google public DNS
  1313. # ip -6 route get 2404:6800:400a:800::1012 -> ipv6.google.com
  1314. # to find out the default outgoing interface, address, and gateway
  1315. command = dict(
  1316. v4 = [ip_path, '-4', 'route', 'get', '8.8.8.8'],
  1317. v6 = [ip_path, '-6', 'route', 'get', '2404:6800:400a:800::1012']
  1318. )
  1319. interface = dict(v4 = {}, v6 = {})
  1320. for v in 'v4', 'v6':
  1321. if v == 'v6' and self.facts['os_family'] == 'RedHat' \
  1322. and self.facts['distribution_version'].startswith('4.'):
  1323. continue
  1324. if v == 'v6' and not socket.has_ipv6:
  1325. continue
  1326. rc, out, err = module.run_command(command[v])
  1327. if not out:
  1328. # v6 routing may result in
  1329. # RTNETLINK answers: Invalid argument
  1330. continue
  1331. words = out.split('\n')[0].split()
  1332. # A valid output starts with the queried address on the first line
  1333. if len(words) > 0 and words[0] == command[v][-1]:
  1334. for i in range(len(words) - 1):
  1335. if words[i] == 'dev':
  1336. interface[v]['interface'] = words[i+1]
  1337. elif words[i] == 'src':
  1338. interface[v]['address'] = words[i+1]
  1339. elif words[i] == 'via' and words[i+1] != command[v][-1]:
  1340. interface[v]['gateway'] = words[i+1]
  1341. return interface['v4'], interface['v6']
  1342. def get_interfaces_info(self, ip_path, default_ipv4, default_ipv6):
  1343. interfaces = {}
  1344. ips = dict(
  1345. all_ipv4_addresses = [],
  1346. all_ipv6_addresses = [],
  1347. )
  1348. for path in glob.glob('/sys/class/net/*'):
  1349. if not os.path.isdir(path):
  1350. continue
  1351. device = os.path.basename(path)
  1352. interfaces[device] = { 'device': device }
  1353. if os.path.exists(os.path.join(path, 'address')):
  1354. macaddress = open(os.path.join(path, 'address')).read().strip()
  1355. if macaddress and macaddress != '00:00:00:00:00:00':
  1356. interfaces[device]['macaddress'] = macaddress
  1357. if os.path.exists(os.path.join(path, 'mtu')):
  1358. interfaces[device]['mtu'] = int(open(os.path.join(path, 'mtu')).read().strip())
  1359. if os.path.exists(os.path.join(path, 'operstate')):
  1360. interfaces[device]['active'] = open(os.path.join(path, 'operstate')).read().strip() != 'down'
  1361. # if os.path.exists(os.path.join(path, 'carrier')):
  1362. # interfaces[device]['link'] = open(os.path.join(path, 'carrier')).read().strip() == '1'
  1363. if os.path.exists(os.path.join(path, 'device','driver', 'module')):
  1364. interfaces[device]['module'] = os.path.basename(os.path.realpath(os.path.join(path, 'device', 'driver', 'module')))
  1365. if os.path.exists(os.path.join(path, 'type')):
  1366. type = open(os.path.join(path, 'type')).read().strip()
  1367. if type == '1':
  1368. interfaces[device]['type'] = 'ether'
  1369. elif type == '512':
  1370. interfaces[device]['type'] = 'ppp'
  1371. elif type == '772':
  1372. interfaces[device]['type'] = 'loopback'
  1373. if os.path.exists(os.path.join(path, 'bridge')):
  1374. interfaces[device]['type'] = 'bridge'
  1375. interfaces[device]['interfaces'] = [ os.path.basename(b) for b in glob.glob(os.path.join(path, 'brif', '*')) ]
  1376. if os.path.exists(os.path.join(path, 'bridge', 'bridge_id')):
  1377. interfaces[device]['id'] = open(os.path.join(path, 'bridge', 'bridge_id')).read().strip()
  1378. if os.path.exists(os.path.join(path, 'bridge', 'stp_state')):
  1379. interfaces[device]['stp'] = open(os.path.join(path, 'bridge', 'stp_state')).read().strip() == '1'
  1380. if os.path.exists(os.path.join(path, 'bonding')):
  1381. interfaces[device]['type'] = 'bonding'
  1382. interfaces[device]['slaves'] = open(os.path.join(path, 'bonding', 'slaves')).read().split()
  1383. interfaces[device]['mode'] = open(os.path.join(path, 'bonding', 'mode')).read().split()[0]
  1384. interfaces[device]['miimon'] = open(os.path.join(path, 'bonding', 'miimon')).read().split()[0]
  1385. interfaces[device]['lacp_rate'] = open(os.path.join(path, 'bonding', 'lacp_rate')).read().split()[0]
  1386. primary = open(os.path.join(path, 'bonding', 'primary')).read()
  1387. if primary:
  1388. interfaces[device]['primary'] = primary
  1389. path = os.path.join(path, 'bonding', 'all_slaves_active')
  1390. if os.path.exists(path):
  1391. interfaces[device]['all_slaves_active'] = open(path).read() == '1'
  1392. # Check whether a interface is in promiscuous mode
  1393. if os.path.exists(os.path.join(path,'flags')):
  1394. promisc_mode = False
  1395. # The second byte indicates whether the interface is in promiscuous mode.
  1396. # 1 = promisc
  1397. # 0 = no promisc
  1398. data = int(open(os.path.join(path, 'flags')).read().strip(),16)
  1399. promisc_mode = (data & 0x0100 > 0)
  1400. interfaces[device]['promisc'] = promisc_mode
  1401. def parse_ip_output(output, secondary=False):
  1402. for line in output.split('\n'):
  1403. if not line:
  1404. continue
  1405. words = line.split()
  1406. if words[0] == 'inet':
  1407. if '/' in words[1]:
  1408. address, netmask_length = words[1].split('/')
  1409. else:
  1410. # pointopoint interfaces do not have a prefix
  1411. address = words[1]
  1412. netmask_length = "32"
  1413. address_bin = struct.unpack('!L', socket.inet_aton(address))[0]
  1414. netmask_bin = (1<<32) - (1<<32>>int(netmask_length))
  1415. netmask = socket.inet_ntoa(struct.pack('!L', netmask_bin))
  1416. network = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin))
  1417. iface = words[-1]
  1418. if iface != device:
  1419. interfaces[iface] = {}
  1420. if not secondary or "ipv4" not in interfaces[iface]:
  1421. interfaces[iface]['ipv4'] = {'address': address,
  1422. 'netmask': netmask,
  1423. 'network': network}
  1424. else:
  1425. if "ipv4_secondaries" not in interfaces[iface]:
  1426. interfaces[iface]["ipv4_secondaries"] = []
  1427. interfaces[iface]["ipv4_secondaries"].append({
  1428. 'address': address,
  1429. 'netmask': netmask,
  1430. 'network': network,
  1431. })
  1432. # add this secondary IP to the main device
  1433. if secondary:
  1434. if "ipv4_secondaries" not in interfaces[device]:
  1435. interfaces[device]["ipv4_secondaries"] = []
  1436. interfaces[device]["ipv4_secondaries"].append({
  1437. 'address': address,
  1438. 'netmask': netmask,
  1439. 'network': network,
  1440. })
  1441. # If this is the default address, update default_ipv4
  1442. if 'address' in default_ipv4 and default_ipv4['address'] == address:
  1443. default_ipv4['netmask'] = netmask
  1444. default_ipv4['network'] = network
  1445. default_ipv4['macaddress'] = macaddress
  1446. default_ipv4['mtu'] = interfaces[device]['mtu']
  1447. default_ipv4['type'] = interfaces[device].get("type", "unknown")
  1448. default_ipv4['alias'] = words[-1]
  1449. if not address.startswith('127.'):
  1450. ips['all_ipv4_addresses'].append(address)
  1451. elif words[0] == 'inet6':
  1452. address, prefix = words[1].split('/')
  1453. scope = words[3]
  1454. if 'ipv6' not in interfaces[device]:
  1455. interfaces[device]['ipv6'] = []
  1456. interfaces[device]['ipv6'].append({
  1457. 'address' : address,
  1458. 'prefix' : prefix,
  1459. 'scope' : scope
  1460. })
  1461. # If this is the default address, update default_ipv6
  1462. if 'address' in default_ipv6 and default_ipv6['address'] == address:
  1463. default_ipv6['prefix'] = prefix
  1464. default_ipv6['scope'] = scope
  1465. default_ipv6['macaddress'] = macaddress
  1466. default_ipv6['mtu'] = interfaces[device]['mtu']
  1467. default_ipv6['type'] = interfaces[device].get("type", "unknown")
  1468. if not address == '::1':
  1469. ips['all_ipv6_addresses'].append(address)
  1470. ip_path = module.get_bin_path("ip")
  1471. args = [ip_path, 'addr', 'show', 'primary', device]
  1472. rc, stdout, stderr = self.module.run_command(args)
  1473. primary_data = stdout
  1474. args = [ip_path, 'addr', 'show', 'secondary', device]
  1475. rc, stdout, stderr = self.module.run_command(args)
  1476. secondary_data = stdout
  1477. parse_ip_output(primary_data)
  1478. parse_ip_output(secondary_data, secondary=True)
  1479. # replace : by _ in interface name since they are hard to use in template
  1480. new_interfaces = {}
  1481. for i in interfaces:
  1482. if ':' in i:
  1483. new_interfaces[i.replace(':','_')] = interfaces[i]
  1484. else:
  1485. new_interfaces[i] = interfaces[i]
  1486. return new_interfaces, ips
  1487. class GenericBsdIfconfigNetwork(Network):
  1488. """
  1489. This is a generic BSD subclass of Network using the ifconfig command.
  1490. It defines
  1491. - interfaces (a list of interface names)
  1492. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1493. - all_ipv4_addresses and all_ipv6_addresses: lists of all configured addresses.
  1494. It currently does not define
  1495. - default_ipv4 and default_ipv6
  1496. - type, mtu and network on interfaces
  1497. """
  1498. platform = 'Generic_BSD_Ifconfig'
  1499. def __init__(self, module):
  1500. Network.__init__(self, module)
  1501. def populate(self):
  1502. ifconfig_path = module.get_bin_path('ifconfig')
  1503. if ifconfig_path is None:
  1504. return self.facts
  1505. route_path = module.get_bin_path('route')
  1506. if route_path is None:
  1507. return self.facts
  1508. default_ipv4, default_ipv6 = self.get_default_interfaces(route_path)
  1509. interfaces, ips = self.get_interfaces_info(ifconfig_path)
  1510. self.merge_default_interface(default_ipv4, interfaces, 'ipv4')
  1511. self.merge_default_interface(default_ipv6, interfaces, 'ipv6')
  1512. self.facts['interfaces'] = interfaces.keys()
  1513. for iface in interfaces:
  1514. self.facts[iface] = interfaces[iface]
  1515. self.facts['default_ipv4'] = default_ipv4
  1516. self.facts['default_ipv6'] = default_ipv6
  1517. self.facts['all_ipv4_addresses'] = ips['all_ipv4_addresses']
  1518. self.facts['all_ipv6_addresses'] = ips['all_ipv6_addresses']
  1519. return self.facts
  1520. def get_default_interfaces(self, route_path):
  1521. # Use the commands:
  1522. # route -n get 8.8.8.8 -> Google public DNS
  1523. # route -n get -inet6 2404:6800:400a:800::1012 -> ipv6.google.com
  1524. # to find out the default outgoing interface, address, and gateway
  1525. command = dict(
  1526. v4 = [route_path, '-n', 'get', '8.8.8.8'],
  1527. v6 = [route_path, '-n', 'get', '-inet6', '2404:6800:400a:800::1012']
  1528. )
  1529. interface = dict(v4 = {}, v6 = {})
  1530. for v in 'v4', 'v6':
  1531. if v == 'v6' and not socket.has_ipv6:
  1532. continue
  1533. rc, out, err = module.run_command(command[v])
  1534. if not out:
  1535. # v6 routing may result in
  1536. # RTNETLINK answers: Invalid argument
  1537. continue
  1538. lines = out.split('\n')
  1539. for line in lines:
  1540. words = line.split()
  1541. # Collect output from route command
  1542. if len(words) > 1:
  1543. if words[0] == 'interface:':
  1544. interface[v]['interface'] = words[1]
  1545. if words[0] == 'gateway:':
  1546. interface[v]['gateway'] = words[1]
  1547. return interface['v4'], interface['v6']
  1548. def get_interfaces_info(self, ifconfig_path):
  1549. interfaces = {}
  1550. current_if = {}
  1551. ips = dict(
  1552. all_ipv4_addresses = [],
  1553. all_ipv6_addresses = [],
  1554. )
  1555. # FreeBSD, DragonflyBSD, NetBSD, OpenBSD and OS X all implicitly add '-a'
  1556. # when running the command 'ifconfig'.
  1557. # Solaris must explicitly run the command 'ifconfig -a'.
  1558. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1559. for line in out.split('\n'):
  1560. if line:
  1561. words = line.split()
  1562. if words[0] == 'pass':
  1563. continue
  1564. elif re.match('^\S', line) and len(words) > 3:
  1565. current_if = self.parse_interface_line(words)
  1566. interfaces[ current_if['device'] ] = current_if
  1567. elif words[0].startswith('options='):
  1568. self.parse_options_line(words, current_if, ips)
  1569. elif words[0] == 'nd6':
  1570. self.parse_nd6_line(words, current_if, ips)
  1571. elif words[0] == 'ether':
  1572. self.parse_ether_line(words, current_if, ips)
  1573. elif words[0] == 'media:':
  1574. self.parse_media_line(words, current_if, ips)
  1575. elif words[0] == 'status:':
  1576. self.parse_status_line(words, current_if, ips)
  1577. elif words[0] == 'lladdr':
  1578. self.parse_lladdr_line(words, current_if, ips)
  1579. elif words[0] == 'inet':
  1580. self.parse_inet_line(words, current_if, ips)
  1581. elif words[0] == 'inet6':
  1582. self.parse_inet6_line(words, current_if, ips)
  1583. else:
  1584. self.parse_unknown_line(words, current_if, ips)
  1585. return interfaces, ips
  1586. def parse_interface_line(self, words):
  1587. device = words[0][0:-1]
  1588. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  1589. current_if['flags'] = self.get_options(words[1])
  1590. current_if['macaddress'] = 'unknown' # will be overwritten later
  1591. if len(words) >= 5 : # Newer FreeBSD versions
  1592. current_if['metric'] = words[3]
  1593. current_if['mtu'] = words[5]
  1594. else:
  1595. current_if['mtu'] = words[3]
  1596. return current_if
  1597. def parse_options_line(self, words, current_if, ips):
  1598. # Mac has options like this...
  1599. current_if['options'] = self.get_options(words[0])
  1600. def parse_nd6_line(self, words, current_if, ips):
  1601. # FreBSD has options like this...
  1602. current_if['options'] = self.get_options(words[1])
  1603. def parse_ether_line(self, words, current_if, ips):
  1604. current_if['macaddress'] = words[1]
  1605. def parse_media_line(self, words, current_if, ips):
  1606. # not sure if this is useful - we also drop information
  1607. current_if['media'] = words[1]
  1608. if len(words) > 2:
  1609. current_if['media_select'] = words[2]
  1610. if len(words) > 3:
  1611. current_if['media_type'] = words[3][1:]
  1612. if len(words) > 4:
  1613. current_if['media_options'] = self.get_options(words[4])
  1614. def parse_status_line(self, words, current_if, ips):
  1615. current_if['status'] = words[1]
  1616. def parse_lladdr_line(self, words, current_if, ips):
  1617. current_if['lladdr'] = words[1]
  1618. def parse_inet_line(self, words, current_if, ips):
  1619. address = {'address': words[1]}
  1620. # deal with hex netmask
  1621. if re.match('([0-9a-f]){8}', words[3]) and len(words[3]) == 8:
  1622. words[3] = '0x' + words[3]
  1623. if words[3].startswith('0x'):
  1624. address['netmask'] = socket.inet_ntoa(struct.pack('!L', int(words[3], base=16)))
  1625. else:
  1626. # otherwise assume this is a dotted quad
  1627. address['netmask'] = words[3]
  1628. # calculate the network
  1629. address_bin = struct.unpack('!L', socket.inet_aton(address['address']))[0]
  1630. netmask_bin = struct.unpack('!L', socket.inet_aton(address['netmask']))[0]
  1631. address['network'] = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin))
  1632. # broadcast may be given or we need to calculate
  1633. if len(words) > 5:
  1634. address['broadcast'] = words[5]
  1635. else:
  1636. address['broadcast'] = socket.inet_ntoa(struct.pack('!L', address_bin | (~netmask_bin & 0xffffffff)))
  1637. # add to our list of addresses
  1638. if not words[1].startswith('127.'):
  1639. ips['all_ipv4_addresses'].append(address['address'])
  1640. current_if['ipv4'].append(address)
  1641. def parse_inet6_line(self, words, current_if, ips):
  1642. address = {'address': words[1]}
  1643. if (len(words) >= 4) and (words[2] == 'prefixlen'):
  1644. address['prefix'] = words[3]
  1645. if (len(words) >= 6) and (words[4] == 'scopeid'):
  1646. address['scope'] = words[5]
  1647. localhost6 = ['::1', '::1/128', 'fe80::1%lo0']
  1648. if address['address'] not in localhost6:
  1649. ips['all_ipv6_addresses'].append(address['address'])
  1650. current_if['ipv6'].append(address)
  1651. def parse_unknown_line(self, words, current_if, ips):
  1652. # we are going to ignore unknown lines here - this may be
  1653. # a bad idea - but you can override it in your subclass
  1654. pass
  1655. def get_options(self, option_string):
  1656. start = option_string.find('<') + 1
  1657. end = option_string.rfind('>')
  1658. if (start > 0) and (end > 0) and (end > start + 1):
  1659. option_csv = option_string[start:end]
  1660. return option_csv.split(',')
  1661. else:
  1662. return []
  1663. def merge_default_interface(self, defaults, interfaces, ip_type):
  1664. if not 'interface' in defaults.keys():
  1665. return
  1666. if not defaults['interface'] in interfaces:
  1667. return
  1668. ifinfo = interfaces[defaults['interface']]
  1669. # copy all the interface values across except addresses
  1670. for item in ifinfo.keys():
  1671. if item != 'ipv4' and item != 'ipv6':
  1672. defaults[item] = ifinfo[item]
  1673. if len(ifinfo[ip_type]) > 0:
  1674. for item in ifinfo[ip_type][0].keys():
  1675. defaults[item] = ifinfo[ip_type][0][item]
  1676. class DarwinNetwork(GenericBsdIfconfigNetwork, Network):
  1677. """
  1678. This is the Mac OS X/Darwin Network Class.
  1679. It uses the GenericBsdIfconfigNetwork unchanged
  1680. """
  1681. platform = 'Darwin'
  1682. # media line is different to the default FreeBSD one
  1683. def parse_media_line(self, words, current_if, ips):
  1684. # not sure if this is useful - we also drop information
  1685. current_if['media'] = 'Unknown' # Mac does not give us this
  1686. current_if['media_select'] = words[1]
  1687. if len(words) > 2:
  1688. current_if['media_type'] = words[2][1:]
  1689. if len(words) > 3:
  1690. current_if['media_options'] = self.get_options(words[3])
  1691. class FreeBSDNetwork(GenericBsdIfconfigNetwork, Network):
  1692. """
  1693. This is the FreeBSD Network Class.
  1694. It uses the GenericBsdIfconfigNetwork unchanged.
  1695. """
  1696. platform = 'FreeBSD'
  1697. class AIXNetwork(GenericBsdIfconfigNetwork, Network):
  1698. """
  1699. This is the AIX Network Class.
  1700. It uses the GenericBsdIfconfigNetwork unchanged.
  1701. """
  1702. platform = 'AIX'
  1703. # AIX 'ifconfig -a' does not have three words in the interface line
  1704. def get_interfaces_info(self, ifconfig_path):
  1705. interfaces = {}
  1706. current_if = {}
  1707. ips = dict(
  1708. all_ipv4_addresses = [],
  1709. all_ipv6_addresses = [],
  1710. )
  1711. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1712. for line in out.split('\n'):
  1713. if line:
  1714. words = line.split()
  1715. # only this condition differs from GenericBsdIfconfigNetwork
  1716. if re.match('^\w*\d*:', line):
  1717. current_if = self.parse_interface_line(words)
  1718. interfaces[ current_if['device'] ] = current_if
  1719. elif words[0].startswith('options='):
  1720. self.parse_options_line(words, current_if, ips)
  1721. elif words[0] == 'nd6':
  1722. self.parse_nd6_line(words, current_if, ips)
  1723. elif words[0] == 'ether':
  1724. self.parse_ether_line(words, current_if, ips)
  1725. elif words[0] == 'media:':
  1726. self.parse_media_line(words, current_if, ips)
  1727. elif words[0] == 'status:':
  1728. self.parse_status_line(words, current_if, ips)
  1729. elif words[0] == 'lladdr':
  1730. self.parse_lladdr_line(words, current_if, ips)
  1731. elif words[0] == 'inet':
  1732. self.parse_inet_line(words, current_if, ips)
  1733. elif words[0] == 'inet6':
  1734. self.parse_inet6_line(words, current_if, ips)
  1735. else:
  1736. self.parse_unknown_line(words, current_if, ips)
  1737. return interfaces, ips
  1738. # AIX 'ifconfig -a' does not inform about MTU, so remove current_if['mtu'] here
  1739. def parse_interface_line(self, words):
  1740. device = words[0][0:-1]
  1741. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  1742. current_if['flags'] = self.get_options(words[1])
  1743. current_if['macaddress'] = 'unknown' # will be overwritten later
  1744. return current_if
  1745. class OpenBSDNetwork(GenericBsdIfconfigNetwork, Network):
  1746. """
  1747. This is the OpenBSD Network Class.
  1748. It uses the GenericBsdIfconfigNetwork.
  1749. """
  1750. platform = 'OpenBSD'
  1751. # Return macaddress instead of lladdr
  1752. def parse_lladdr_line(self, words, current_if, ips):
  1753. current_if['macaddress'] = words[1]
  1754. class SunOSNetwork(GenericBsdIfconfigNetwork, Network):
  1755. """
  1756. This is the SunOS Network Class.
  1757. It uses the GenericBsdIfconfigNetwork.
  1758. Solaris can have different FLAGS and MTU for IPv4 and IPv6 on the same interface
  1759. so these facts have been moved inside the 'ipv4' and 'ipv6' lists.
  1760. """
  1761. platform = 'SunOS'
  1762. # Solaris 'ifconfig -a' will print interfaces twice, once for IPv4 and again for IPv6.
  1763. # MTU and FLAGS also may differ between IPv4 and IPv6 on the same interface.
  1764. # 'parse_interface_line()' checks for previously seen interfaces before defining
  1765. # 'current_if' so that IPv6 facts don't clobber IPv4 facts (or vice versa).
  1766. def get_interfaces_info(self, ifconfig_path):
  1767. interfaces = {}
  1768. current_if = {}
  1769. ips = dict(
  1770. all_ipv4_addresses = [],
  1771. all_ipv6_addresses = [],
  1772. )
  1773. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1774. for line in out.split('\n'):
  1775. if line:
  1776. words = line.split()
  1777. if re.match('^\S', line) and len(words) > 3:
  1778. current_if = self.parse_interface_line(words, current_if, interfaces)
  1779. interfaces[ current_if['device'] ] = current_if
  1780. elif words[0].startswith('options='):
  1781. self.parse_options_line(words, current_if, ips)
  1782. elif words[0] == 'nd6':
  1783. self.parse_nd6_line(words, current_if, ips)
  1784. elif words[0] == 'ether':
  1785. self.parse_ether_line(words, current_if, ips)
  1786. elif words[0] == 'media:':
  1787. self.parse_media_line(words, current_if, ips)
  1788. elif words[0] == 'status:':
  1789. self.parse_status_line(words, current_if, ips)
  1790. elif words[0] == 'lladdr':
  1791. self.parse_lladdr_line(words, current_if, ips)
  1792. elif words[0] == 'inet':
  1793. self.parse_inet_line(words, current_if, ips)
  1794. elif words[0] == 'inet6':
  1795. self.parse_inet6_line(words, current_if, ips)
  1796. else:
  1797. self.parse_unknown_line(words, current_if, ips)
  1798. # 'parse_interface_line' and 'parse_inet*_line' leave two dicts in the
  1799. # ipv4/ipv6 lists which is ugly and hard to read.
  1800. # This quick hack merges the dictionaries. Purely cosmetic.
  1801. for iface in interfaces:
  1802. for v in 'ipv4', 'ipv6':
  1803. combined_facts = {}
  1804. for facts in interfaces[iface][v]:
  1805. combined_facts.update(facts)
  1806. if len(combined_facts.keys()) > 0:
  1807. interfaces[iface][v] = [combined_facts]
  1808. return interfaces, ips
  1809. def parse_interface_line(self, words, current_if, interfaces):
  1810. device = words[0][0:-1]
  1811. if device not in interfaces.keys():
  1812. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  1813. else:
  1814. current_if = interfaces[device]
  1815. flags = self.get_options(words[1])
  1816. if 'IPv4' in flags:
  1817. v = 'ipv4'
  1818. if 'IPv6' in flags:
  1819. v = 'ipv6'
  1820. current_if[v].append({'flags': flags, 'mtu': words[3]})
  1821. current_if['macaddress'] = 'unknown' # will be overwritten later
  1822. return current_if
  1823. # Solaris displays single digit octets in MAC addresses e.g. 0:1:2:d:e:f
  1824. # Add leading zero to each octet where needed.
  1825. def parse_ether_line(self, words, current_if, ips):
  1826. macaddress = ''
  1827. for octet in words[1].split(':'):
  1828. octet = ('0' + octet)[-2:None]
  1829. macaddress += (octet + ':')
  1830. current_if['macaddress'] = macaddress[0:-1]
  1831. class Virtual(Facts):
  1832. """
  1833. This is a generic Virtual subclass of Facts. This should be further
  1834. subclassed to implement per platform. If you subclass this,
  1835. you should define:
  1836. - virtualization_type
  1837. - virtualization_role
  1838. - container (e.g. solaris zones, freebsd jails, linux containers)
  1839. All subclasses MUST define platform.
  1840. """
  1841. def __new__(cls, *arguments, **keyword):
  1842. subclass = cls
  1843. for sc in Virtual.__subclasses__():
  1844. if sc.platform == platform.system():
  1845. subclass = sc
  1846. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  1847. def __init__(self):
  1848. Facts.__init__(self)
  1849. def populate(self):
  1850. return self.facts
  1851. class LinuxVirtual(Virtual):
  1852. """
  1853. This is a Linux-specific subclass of Virtual. It defines
  1854. - virtualization_type
  1855. - virtualization_role
  1856. """
  1857. platform = 'Linux'
  1858. def __init__(self):
  1859. Virtual.__init__(self)
  1860. def populate(self):
  1861. self.get_virtual_facts()
  1862. return self.facts
  1863. # For more information, check: http://people.redhat.com/~rjones/virt-what/
  1864. def get_virtual_facts(self):
  1865. if os.path.exists("/proc/xen"):
  1866. self.facts['virtualization_type'] = 'xen'
  1867. self.facts['virtualization_role'] = 'guest'
  1868. try:
  1869. for line in open('/proc/xen/capabilities'):
  1870. if "control_d" in line:
  1871. self.facts['virtualization_role'] = 'host'
  1872. except IOError:
  1873. pass
  1874. return
  1875. if os.path.exists('/proc/vz'):
  1876. self.facts['virtualization_type'] = 'openvz'
  1877. if os.path.exists('/proc/bc'):
  1878. self.facts['virtualization_role'] = 'host'
  1879. else:
  1880. self.facts['virtualization_role'] = 'guest'
  1881. return
  1882. if os.path.exists('/proc/1/cgroup'):
  1883. for line in open('/proc/1/cgroup').readlines():
  1884. if re.search('/lxc/', line):
  1885. self.facts['virtualization_type'] = 'lxc'
  1886. self.facts['virtualization_role'] = 'guest'
  1887. return
  1888. product_name = get_file_content('/sys/devices/virtual/dmi/id/product_name')
  1889. if product_name in ['KVM', 'Bochs']:
  1890. self.facts['virtualization_type'] = 'kvm'
  1891. self.facts['virtualization_role'] = 'guest'
  1892. return
  1893. if product_name == 'RHEV Hypervisor':
  1894. self.facts['virtualization_type'] = 'RHEV'
  1895. self.facts['virtualization_role'] = 'guest'
  1896. return
  1897. if product_name == 'VMware Virtual Platform':
  1898. self.facts['virtualization_type'] = 'VMware'
  1899. self.facts['virtualization_role'] = 'guest'
  1900. return
  1901. bios_vendor = get_file_content('/sys/devices/virtual/dmi/id/bios_vendor')
  1902. if bios_vendor == 'Xen':
  1903. self.facts['virtualization_type'] = 'xen'
  1904. self.facts['virtualization_role'] = 'guest'
  1905. return
  1906. if bios_vendor == 'innotek GmbH':
  1907. self.facts['virtualization_type'] = 'virtualbox'
  1908. self.facts['virtualization_role'] = 'guest'
  1909. return
  1910. sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor')
  1911. # FIXME: This does also match hyperv
  1912. if sys_vendor == 'Microsoft Corporation':
  1913. self.facts['virtualization_type'] = 'VirtualPC'
  1914. self.facts['virtualization_role'] = 'guest'
  1915. return
  1916. if sys_vendor == 'Parallels Software International Inc.':
  1917. self.facts['virtualization_type'] = 'parallels'
  1918. self.facts['virtualization_role'] = 'guest'
  1919. return
  1920. if os.path.exists('/proc/self/status'):
  1921. for line in open('/proc/self/status').readlines():
  1922. if re.match('^VxID: \d+', line):
  1923. self.facts['virtualization_type'] = 'linux_vserver'
  1924. if re.match('^VxID: 0', line):
  1925. self.facts['virtualization_role'] = 'host'
  1926. else:
  1927. self.facts['virtualization_role'] = 'guest'
  1928. return
  1929. if os.path.exists('/proc/cpuinfo'):
  1930. for line in open('/proc/cpuinfo').readlines():
  1931. if re.match('^model name.*QEMU Virtual CPU', line):
  1932. self.facts['virtualization_type'] = 'kvm'
  1933. elif re.match('^vendor_id.*User Mode Linux', line):
  1934. self.facts['virtualization_type'] = 'uml'
  1935. elif re.match('^model name.*UML', line):
  1936. self.facts['virtualization_type'] = 'uml'
  1937. elif re.match('^vendor_id.*PowerVM Lx86', line):
  1938. self.facts['virtualization_type'] = 'powervm_lx86'
  1939. elif re.match('^vendor_id.*IBM/S390', line):
  1940. self.facts['virtualization_type'] = 'ibm_systemz'
  1941. else:
  1942. continue
  1943. self.facts['virtualization_role'] = 'guest'
  1944. return
  1945. # Beware that we can have both kvm and virtualbox running on a single system
  1946. if os.path.exists("/proc/modules") and os.access('/proc/modules', os.R_OK):
  1947. modules = []
  1948. for line in open("/proc/modules").readlines():
  1949. data = line.split(" ", 1)
  1950. modules.append(data[0])
  1951. if 'kvm' in modules:
  1952. self.facts['virtualization_type'] = 'kvm'
  1953. self.facts['virtualization_role'] = 'host'
  1954. return
  1955. if 'vboxdrv' in modules:
  1956. self.facts['virtualization_type'] = 'virtualbox'
  1957. self.facts['virtualization_role'] = 'host'
  1958. return
  1959. # If none of the above matches, return 'NA' for virtualization_type
  1960. # and virtualization_role. This allows for proper grouping.
  1961. self.facts['virtualization_type'] = 'NA'
  1962. self.facts['virtualization_role'] = 'NA'
  1963. return
  1964. class HPUXVirtual(Virtual):
  1965. """
  1966. This is a HP-UX specific subclass of Virtual. It defines
  1967. - virtualization_type
  1968. - virtualization_role
  1969. """
  1970. platform = 'HP-UX'
  1971. def __init__(self):
  1972. Virtual.__init__(self)
  1973. def populate(self):
  1974. self.get_virtual_facts()
  1975. return self.facts
  1976. def get_virtual_facts(self):
  1977. if os.path.exists('/usr/sbin/vecheck'):
  1978. rc, out, err = module.run_command("/usr/sbin/vecheck")
  1979. if rc == 0:
  1980. self.facts['virtualization_type'] = 'guest'
  1981. self.facts['virtualization_role'] = 'HP vPar'
  1982. if os.path.exists('/opt/hpvm/bin/hpvminfo'):
  1983. rc, out, err = module.run_command("/opt/hpvm/bin/hpvminfo")
  1984. if rc == 0 and re.match('.*Running.*HPVM vPar.*', out):
  1985. self.facts['virtualization_type'] = 'guest'
  1986. self.facts['virtualization_role'] = 'HPVM vPar'
  1987. elif rc == 0 and re.match('.*Running.*HPVM guest.*', out):
  1988. self.facts['virtualization_type'] = 'guest'
  1989. self.facts['virtualization_role'] = 'HPVM IVM'
  1990. elif rc == 0 and re.match('.*Running.*HPVM host.*', out):
  1991. self.facts['virtualization_type'] = 'host'
  1992. self.facts['virtualization_role'] = 'HPVM'
  1993. if os.path.exists('/usr/sbin/parstatus'):
  1994. rc, out, err = module.run_command("/usr/sbin/parstatus")
  1995. if rc == 0:
  1996. self.facts['virtualization_type'] = 'guest'
  1997. self.facts['virtualization_role'] = 'HP nPar'
  1998. class SunOSVirtual(Virtual):
  1999. """
  2000. This is a SunOS-specific subclass of Virtual. It defines
  2001. - virtualization_type
  2002. - virtualization_role
  2003. - container
  2004. """
  2005. platform = 'SunOS'
  2006. def __init__(self):
  2007. Virtual.__init__(self)
  2008. def populate(self):
  2009. self.get_virtual_facts()
  2010. return self.facts
  2011. def get_virtual_facts(self):
  2012. rc, out, err = module.run_command("/usr/sbin/prtdiag")
  2013. for line in out.split('\n'):
  2014. if 'VMware' in line:
  2015. self.facts['virtualization_type'] = 'vmware'
  2016. self.facts['virtualization_role'] = 'guest'
  2017. if 'Parallels' in line:
  2018. self.facts['virtualization_type'] = 'parallels'
  2019. self.facts['virtualization_role'] = 'guest'
  2020. if 'VirtualBox' in line:
  2021. self.facts['virtualization_type'] = 'virtualbox'
  2022. self.facts['virtualization_role'] = 'guest'
  2023. if 'HVM domU' in line:
  2024. self.facts['virtualization_type'] = 'xen'
  2025. self.facts['virtualization_role'] = 'guest'
  2026. # Check if it's a zone
  2027. if os.path.exists("/usr/bin/zonename"):
  2028. rc, out, err = module.run_command("/usr/bin/zonename")
  2029. if out.rstrip() != "global":
  2030. self.facts['container'] = 'zone'
  2031. # Check if it's a branded zone (i.e. Solaris 8/9 zone)
  2032. if os.path.isdir('/.SUNWnative'):
  2033. self.facts['container'] = 'zone'
  2034. # If it's a zone check if we can detect if our global zone is itself virtualized.
  2035. # Relies on the "guest tools" (e.g. vmware tools) to be installed
  2036. if 'container' in self.facts and self.facts['container'] == 'zone':
  2037. rc, out, err = module.run_command("/usr/sbin/modinfo")
  2038. for line in out.split('\n'):
  2039. if 'VMware' in line:
  2040. self.facts['virtualization_type'] = 'vmware'
  2041. self.facts['virtualization_role'] = 'guest'
  2042. if 'VirtualBox' in line:
  2043. self.facts['virtualization_type'] = 'virtualbox'
  2044. self.facts['virtualization_role'] = 'guest'
  2045. def get_file_content(path, default=None):
  2046. data = default
  2047. if os.path.exists(path) and os.access(path, os.R_OK):
  2048. data = open(path).read().strip()
  2049. if len(data) == 0:
  2050. data = default
  2051. return data
  2052. def ansible_facts(module):
  2053. facts = {}
  2054. facts.update(Facts().populate())
  2055. facts.update(Hardware().populate())
  2056. facts.update(Network(module).populate())
  2057. facts.update(Virtual().populate())
  2058. return facts
  2059. # ===========================================
  2060. def get_all_facts(module):
  2061. setup_options = dict(module_setup=True)
  2062. facts = ansible_facts(module)
  2063. for (k, v) in facts.items():
  2064. setup_options["ansible_%s" % k.replace('-', '_')] = v
  2065. # Look for the path to the facter and ohai binary and set
  2066. # the variable to that path.
  2067. facter_path = module.get_bin_path('facter')
  2068. ohai_path = module.get_bin_path('ohai')
  2069. # if facter is installed, and we can use --json because
  2070. # ruby-json is ALSO installed, include facter data in the JSON
  2071. if facter_path is not None:
  2072. rc, out, err = module.run_command(facter_path + " --json")
  2073. facter = True
  2074. try:
  2075. facter_ds = json.loads(out)
  2076. except:
  2077. facter = False
  2078. if facter:
  2079. for (k,v) in facter_ds.items():
  2080. setup_options["facter_%s" % k] = v
  2081. # ditto for ohai
  2082. if ohai_path is not None:
  2083. rc, out, err = module.run_command(ohai_path)
  2084. ohai = True
  2085. try:
  2086. ohai_ds = json.loads(out)
  2087. except:
  2088. ohai = False
  2089. if ohai:
  2090. for (k,v) in ohai_ds.items():
  2091. k2 = "ohai_%s" % k.replace('-', '_')
  2092. setup_options[k2] = v
  2093. setup_result = { 'ansible_facts': {} }
  2094. for (k,v) in setup_options.items():
  2095. if module.params['filter'] == '*' or fnmatch.fnmatch(k, module.params['filter']):
  2096. setup_result['ansible_facts'][k] = v
  2097. # hack to keep --verbose from showing all the setup module results
  2098. setup_result['verbose_override'] = True
  2099. return setup_result