palvarez89

Untitled

Jun 30th, 2014
444
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 95.33 KB | None | 0 0
  1. # (c) 2012, Michael DeHaan <[email protected]>
  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.  
  18. import os
  19. import array
  20. import errno
  21. import fcntl
  22. import fnmatch
  23. import glob
  24. import platform
  25. import re
  26. import signal
  27. import socket
  28. import struct
  29. import datetime
  30. import getpass
  31. import ConfigParser
  32. import StringIO
  33.  
  34. try:
  35. import selinux
  36. HAVE_SELINUX=True
  37. except ImportError:
  38. HAVE_SELINUX=False
  39.  
  40. try:
  41. import json
  42. except ImportError:
  43. import simplejson as json
  44.  
  45. # --------------------------------------------------------------
  46. # timeout function to make sure some fact gathering
  47. # steps do not exceed a time limit
  48.  
  49. class TimeoutError(Exception):
  50. pass
  51.  
  52. def timeout(seconds=10, error_message="Timer expired"):
  53. def decorator(func):
  54. def _handle_timeout(signum, frame):
  55. raise TimeoutError(error_message)
  56.  
  57. def wrapper(*args, **kwargs):
  58. signal.signal(signal.SIGALRM, _handle_timeout)
  59. signal.alarm(seconds)
  60. try:
  61. result = func(*args, **kwargs)
  62. finally:
  63. signal.alarm(0)
  64. return result
  65.  
  66. return wrapper
  67.  
  68. return decorator
  69.  
  70. # --------------------------------------------------------------
  71.  
  72. class Facts(object):
  73. """
  74. This class should only attempt to populate those facts that
  75. are mostly generic to all systems. This includes platform facts,
  76. service facts (eg. ssh keys or selinux), and distribution facts.
  77. Anything that requires extensive code or may have more than one
  78. possible implementation to establish facts for a given topic should
  79. subclass Facts.
  80. """
  81.  
  82. _I386RE = re.compile(r'i[3456]86')
  83. # For the most part, we assume that platform.dist() will tell the truth.
  84. # This is the fallback to handle unknowns or exceptions
  85. OSDIST_DICT = { '/etc/redhat-release': 'RedHat',
  86. '/etc/vmware-release': 'VMwareESX',
  87. '/etc/openwrt_release': 'OpenWrt',
  88. '/etc/system-release': 'OtherLinux',
  89. '/etc/alpine-release': 'Alpine',
  90. '/etc/release': 'Solaris',
  91. '/etc/arch-release': 'Archlinux',
  92. '/etc/SuSE-release': 'SuSE',
  93. '/etc/gentoo-release': 'Gentoo',
  94. '/etc/os-release': 'Debian' }
  95. SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' }
  96.  
  97. # A list of dicts. If there is a platform with more than one
  98. # package manager, put the preferred one last. If there is an
  99. # ansible module, use that as the value for the 'name' key.
  100. PKG_MGRS = [ { 'path' : '/usr/bin/yum', 'name' : 'yum' },
  101. { 'path' : '/usr/bin/apt-get', 'name' : 'apt' },
  102. { 'path' : '/usr/bin/zypper', 'name' : 'zypper' },
  103. { 'path' : '/usr/sbin/urpmi', 'name' : 'urpmi' },
  104. { 'path' : '/usr/bin/pacman', 'name' : 'pacman' },
  105. { 'path' : '/bin/opkg', 'name' : 'opkg' },
  106. { 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' },
  107. { 'path' : '/opt/local/bin/port', 'name' : 'macports' },
  108. { 'path' : '/sbin/apk', 'name' : 'apk' },
  109. { 'path' : '/usr/sbin/pkg', 'name' : 'pkgng' },
  110. { 'path' : '/usr/sbin/swlist', 'name' : 'SD-UX' },
  111. { 'path' : '/usr/bin/emerge', 'name' : 'portage' },
  112. ]
  113.  
  114. def __init__(self):
  115. self.facts = {}
  116. self.get_platform_facts()
  117. self.get_distribution_facts()
  118. self.get_cmdline()
  119. self.get_public_ssh_host_keys()
  120. self.get_selinux_facts()
  121. self.get_pkg_mgr_facts()
  122. self.get_lsb_facts()
  123. self.get_date_time_facts()
  124. self.get_user_facts()
  125. self.get_local_facts()
  126. self.get_env_facts()
  127.  
  128. def populate(self):
  129. return self.facts
  130.  
  131. # Platform
  132. # platform.system() can be Linux, Darwin, Java, or Windows
  133. def get_platform_facts(self):
  134. self.facts['system'] = platform.system()
  135. self.facts['kernel'] = platform.release()
  136. self.facts['machine'] = platform.machine()
  137. self.facts['python_version'] = platform.python_version()
  138. self.facts['fqdn'] = socket.getfqdn()
  139. self.facts['hostname'] = platform.node().split('.')[0]
  140. self.facts['nodename'] = platform.node()
  141. self.facts['domain'] = '.'.join(self.facts['fqdn'].split('.')[1:])
  142. arch_bits = platform.architecture()[0]
  143. self.facts['userspace_bits'] = arch_bits.replace('bit', '')
  144. if self.facts['machine'] == 'x86_64':
  145. self.facts['architecture'] = self.facts['machine']
  146. if self.facts['userspace_bits'] == '64':
  147. self.facts['userspace_architecture'] = 'x86_64'
  148. elif self.facts['userspace_bits'] == '32':
  149. self.facts['userspace_architecture'] = 'i386'
  150. elif Facts._I386RE.search(self.facts['machine']):
  151. self.facts['architecture'] = 'i386'
  152. if self.facts['userspace_bits'] == '64':
  153. self.facts['userspace_architecture'] = 'x86_64'
  154. elif self.facts['userspace_bits'] == '32':
  155. self.facts['userspace_architecture'] = 'i386'
  156. else:
  157. self.facts['architecture'] = self.facts['machine']
  158. if self.facts['system'] == 'Linux':
  159. self.get_distribution_facts()
  160. elif self.facts['system'] == 'AIX':
  161. rc, out, err = module.run_command("/usr/sbin/bootinfo -p")
  162. data = out.split('\n')
  163. self.facts['architecture'] = data[0]
  164.  
  165.  
  166. def get_local_facts(self):
  167.  
  168. fact_path = module.params.get('fact_path', None)
  169. if not fact_path or not os.path.exists(fact_path):
  170. return
  171.  
  172. local = {}
  173. for fn in sorted(glob.glob(fact_path + '/*.fact')):
  174. # where it will sit under local facts
  175. fact_base = os.path.basename(fn).replace('.fact','')
  176. if os.access(fn, os.X_OK):
  177. # run it
  178. # try to read it as json first
  179. # if that fails read it with ConfigParser
  180. # if that fails, skip it
  181. rc, out, err = module.run_command(fn)
  182. else:
  183. out = open(fn).read()
  184.  
  185. # load raw json
  186. fact = 'loading %s' % fact_base
  187. try:
  188. fact = json.loads(out)
  189. except ValueError, e:
  190. # load raw ini
  191. cp = ConfigParser.ConfigParser()
  192. try:
  193. cp.readfp(StringIO.StringIO(out))
  194. except ConfigParser.Error, e:
  195. fact="error loading fact - please check content"
  196. else:
  197. fact = {}
  198. #print cp.sections()
  199. for sect in cp.sections():
  200. if sect not in fact:
  201. fact[sect] = {}
  202. for opt in cp.options(sect):
  203. val = cp.get(sect, opt)
  204. fact[sect][opt]=val
  205.  
  206. local[fact_base] = fact
  207. if not local:
  208. return
  209. self.facts['local'] = local
  210.  
  211. # platform.dist() is deprecated in 2.6
  212. # in 2.6 and newer, you should use platform.linux_distribution()
  213. def get_distribution_facts(self):
  214.  
  215. # A list with OS Family members
  216. OS_FAMILY = dict(
  217. RedHat = 'RedHat', Fedora = 'RedHat', CentOS = 'RedHat', Scientific = 'RedHat',
  218. SLC = 'RedHat', Ascendos = 'RedHat', CloudLinux = 'RedHat', PSBM = 'RedHat',
  219. OracleLinux = 'RedHat', OVS = 'RedHat', OEL = 'RedHat', Amazon = 'RedHat',
  220. XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', SLES = 'Suse',
  221. SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo',
  222. Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
  223. Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris',
  224. SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin',
  225. FreeBSD = 'FreeBSD', HPUX = 'HP-UX'
  226. )
  227.  
  228. if self.facts['system'] == 'AIX':
  229. self.facts['distribution'] = 'AIX'
  230. rc, out, err = module.run_command("/usr/bin/oslevel")
  231. data = out.split('.')
  232. self.facts['distribution_version'] = data[0]
  233. self.facts['distribution_release'] = data[1]
  234. elif self.facts['system'] == 'HP-UX':
  235. self.facts['distribution'] = 'HP-UX'
  236. rc, out, err = module.run_command("/usr/sbin/swlist |egrep 'HPUX.*OE.*[AB].[0-9]+\.[0-9]+'", use_unsafe_shell=True)
  237. data = re.search('HPUX.*OE.*([AB].[0-9]+\.[0-9]+)\.([0-9]+).*', out)
  238. if data:
  239. self.facts['distribution_version'] = data.groups()[0]
  240. self.facts['distribution_release'] = data.groups()[1]
  241. elif self.facts['system'] == 'Darwin':
  242. self.facts['distribution'] = 'MacOSX'
  243. rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion")
  244. data = out.split()[-1]
  245. self.facts['distribution_version'] = data
  246. elif self.facts['system'] == 'FreeBSD':
  247. self.facts['distribution'] = 'FreeBSD'
  248. self.facts['distribution_release'] = platform.release()
  249. self.facts['distribution_version'] = platform.version()
  250. elif self.facts['system'] == 'OpenBSD':
  251. self.facts['distribution'] = 'OpenBSD'
  252. self.facts['distribution_release'] = platform.release()
  253. rc, out, err = module.run_command("/sbin/sysctl -n kern.version")
  254. match = re.match('OpenBSD\s[0-9]+.[0-9]+-(\S+)\s.*', out)
  255. if match:
  256. self.facts['distribution_version'] = match.groups()[0]
  257. else:
  258. self.facts['distribution_version'] = 'release'
  259. else:
  260. dist = platform.dist()
  261. self.facts['distribution'] = dist[0].capitalize() or 'NA'
  262. self.facts['distribution_version'] = dist[1] or 'NA'
  263. self.facts['distribution_major_version'] = dist[1].split('.')[0] or 'NA'
  264. self.facts['distribution_release'] = dist[2] or 'NA'
  265. # Try to handle the exceptions now ...
  266. for (path, name) in Facts.OSDIST_DICT.items():
  267. if os.path.exists(path):
  268. if self.facts['distribution'] == 'Fedora':
  269. pass
  270. elif name == 'RedHat':
  271. data = get_file_content(path)
  272. if 'Red Hat' in data:
  273. self.facts['distribution'] = name
  274. else:
  275. self.facts['distribution'] = data.split()[0]
  276. elif name == 'OtherLinux':
  277. data = get_file_content(path)
  278. if 'Amazon' in data:
  279. self.facts['distribution'] = 'Amazon'
  280. self.facts['distribution_version'] = data.split()[-1]
  281. elif name == 'OpenWrt':
  282. data = get_file_content(path)
  283. if 'OpenWrt' in data:
  284. self.facts['distribution'] = name
  285. version = re.search('DISTRIB_RELEASE="(.*)"', data)
  286. if version:
  287. self.facts['distribution_version'] = version.groups()[0]
  288. release = re.search('DISTRIB_CODENAME="(.*)"', data)
  289. if release:
  290. self.facts['distribution_release'] = release.groups()[0]
  291. elif name == 'Alpine':
  292. data = get_file_content(path)
  293. self.facts['distribution'] = 'Alpine'
  294. self.facts['distribution_version'] = data
  295. elif name == 'Solaris':
  296. data = get_file_content(path).split('\n')[0]
  297. ora_prefix = ''
  298. if 'Oracle Solaris' in data:
  299. data = data.replace('Oracle ','')
  300. ora_prefix = 'Oracle '
  301. self.facts['distribution'] = data.split()[0]
  302. self.facts['distribution_version'] = data.split()[1]
  303. self.facts['distribution_release'] = ora_prefix + data
  304. elif name == 'SuSE':
  305. data = get_file_content(path).splitlines()
  306. for line in data:
  307. if '=' in line:
  308. self.facts['distribution_release'] = line.split('=')[1].strip()
  309. elif name == 'Debian':
  310. data = get_file_content(path).split('\n')[0]
  311. release = re.search("PRETTY_NAME.+ \(?([^ ]+?)\)?\"", data)
  312. if release:
  313. self.facts['distribution_release'] = release.groups()[0]
  314. else:
  315. self.facts['distribution'] = name
  316.  
  317. self.facts['os_family'] = self.facts['distribution']
  318. if self.facts['distribution'] in OS_FAMILY:
  319. self.facts['os_family'] = OS_FAMILY[self.facts['distribution']]
  320.  
  321. def get_cmdline(self):
  322. data = get_file_content('/proc/cmdline')
  323. if data:
  324. self.facts['cmdline'] = {}
  325. for piece in shlex.split(data):
  326. item = piece.split('=', 1)
  327. if len(item) == 1:
  328. self.facts['cmdline'][item[0]] = True
  329. else:
  330. self.facts['cmdline'][item[0]] = item[1]
  331.  
  332. def get_public_ssh_host_keys(self):
  333. dsa_filename = '/etc/ssh/ssh_host_dsa_key.pub'
  334. rsa_filename = '/etc/ssh/ssh_host_rsa_key.pub'
  335. ecdsa_filename = '/etc/ssh/ssh_host_ecdsa_key.pub'
  336.  
  337. if self.facts['system'] == 'Darwin':
  338. dsa_filename = '/etc/ssh_host_dsa_key.pub'
  339. rsa_filename = '/etc/ssh_host_rsa_key.pub'
  340. ecdsa_filename = '/etc/ssh_host_ecdsa_key.pub'
  341. dsa = get_file_content(dsa_filename)
  342. rsa = get_file_content(rsa_filename)
  343. ecdsa = get_file_content(ecdsa_filename)
  344. if dsa is None:
  345. dsa = 'NA'
  346. else:
  347. self.facts['ssh_host_key_dsa_public'] = dsa.split()[1]
  348. if rsa is None:
  349. rsa = 'NA'
  350. else:
  351. self.facts['ssh_host_key_rsa_public'] = rsa.split()[1]
  352. if ecdsa is None:
  353. ecdsa = 'NA'
  354. else:
  355. self.facts['ssh_host_key_ecdsa_public'] = ecdsa.split()[1]
  356.  
  357. def get_pkg_mgr_facts(self):
  358. self.facts['pkg_mgr'] = 'unknown'
  359. for pkg in Facts.PKG_MGRS:
  360. if os.path.exists(pkg['path']):
  361. self.facts['pkg_mgr'] = pkg['name']
  362. if self.facts['system'] == 'OpenBSD':
  363. self.facts['pkg_mgr'] = 'openbsd_pkg'
  364.  
  365. def get_lsb_facts(self):
  366. lsb_path = module.get_bin_path('lsb_release')
  367. if lsb_path:
  368. rc, out, err = module.run_command([lsb_path, "-a"])
  369. if rc == 0:
  370. self.facts['lsb'] = {}
  371. for line in out.split('\n'):
  372. if len(line) < 1:
  373. continue
  374. value = line.split(':', 1)[1].strip()
  375. if 'LSB Version:' in line:
  376. self.facts['lsb']['release'] = value
  377. elif 'Distributor ID:' in line:
  378. self.facts['lsb']['id'] = value
  379. elif 'Description:' in line:
  380. self.facts['lsb']['description'] = value
  381. elif 'Release:' in line:
  382. self.facts['lsb']['release'] = value
  383. elif 'Codename:' in line:
  384. self.facts['lsb']['codename'] = value
  385. if 'lsb' in self.facts and 'release' in self.facts['lsb']:
  386. self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
  387. elif lsb_path is None and os.path.exists('/etc/lsb-release'):
  388. self.facts['lsb'] = {}
  389. f = open('/etc/lsb-release', 'r')
  390. try:
  391. for line in f.readlines():
  392. value = line.split('=',1)[1].strip()
  393. if 'DISTRIB_ID' in line:
  394. self.facts['lsb']['id'] = value
  395. elif 'DISTRIB_RELEASE' in line:
  396. self.facts['lsb']['release'] = value
  397. elif 'DISTRIB_DESCRIPTION' in line:
  398. self.facts['lsb']['description'] = value
  399. elif 'DISTRIB_CODENAME' in line:
  400. self.facts['lsb']['codename'] = value
  401. finally:
  402. f.close()
  403. else:
  404. return self.facts
  405.  
  406. if 'lsb' in self.facts and 'release' in self.facts['lsb']:
  407. self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
  408.  
  409.  
  410. def get_selinux_facts(self):
  411. if not HAVE_SELINUX:
  412. self.facts['selinux'] = False
  413. return
  414. self.facts['selinux'] = {}
  415. if not selinux.is_selinux_enabled():
  416. self.facts['selinux']['status'] = 'disabled'
  417. else:
  418. self.facts['selinux']['status'] = 'enabled'
  419. try:
  420. self.facts['selinux']['policyvers'] = selinux.security_policyvers()
  421. except OSError, e:
  422. self.facts['selinux']['policyvers'] = 'unknown'
  423. try:
  424. (rc, configmode) = selinux.selinux_getenforcemode()
  425. if rc == 0:
  426. self.facts['selinux']['config_mode'] = Facts.SELINUX_MODE_DICT.get(configmode, 'unknown')
  427. else:
  428. self.facts['selinux']['config_mode'] = 'unknown'
  429. except OSError, e:
  430. self.facts['selinux']['config_mode'] = 'unknown'
  431. try:
  432. mode = selinux.security_getenforce()
  433. self.facts['selinux']['mode'] = Facts.SELINUX_MODE_DICT.get(mode, 'unknown')
  434. except OSError, e:
  435. self.facts['selinux']['mode'] = 'unknown'
  436. try:
  437. (rc, policytype) = selinux.selinux_getpolicytype()
  438. if rc == 0:
  439. self.facts['selinux']['type'] = policytype
  440. else:
  441. self.facts['selinux']['type'] = 'unknown'
  442. except OSError, e:
  443. self.facts['selinux']['type'] = 'unknown'
  444.  
  445.  
  446. def get_date_time_facts(self):
  447. self.facts['date_time'] = {}
  448.  
  449. now = datetime.datetime.now()
  450. self.facts['date_time']['year'] = now.strftime('%Y')
  451. self.facts['date_time']['month'] = now.strftime('%m')
  452. self.facts['date_time']['weekday'] = now.strftime('%A')
  453. self.facts['date_time']['day'] = now.strftime('%d')
  454. self.facts['date_time']['hour'] = now.strftime('%H')
  455. self.facts['date_time']['minute'] = now.strftime('%M')
  456. self.facts['date_time']['second'] = now.strftime('%S')
  457. self.facts['date_time']['epoch'] = now.strftime('%s')
  458. if self.facts['date_time']['epoch'] == '' or self.facts['date_time']['epoch'][0] == '%':
  459. self.facts['date_time']['epoch'] = str(int(time.time()))
  460. self.facts['date_time']['date'] = now.strftime('%Y-%m-%d')
  461. self.facts['date_time']['time'] = now.strftime('%H:%M:%S')
  462. self.facts['date_time']['iso8601_micro'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
  463. self.facts['date_time']['iso8601'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
  464. self.facts['date_time']['tz'] = time.strftime("%Z")
  465. self.facts['date_time']['tz_offset'] = time.strftime("%z")
  466.  
  467.  
  468. # User
  469. def get_user_facts(self):
  470. self.facts['user_id'] = getpass.getuser()
  471.  
  472. def get_env_facts(self):
  473. self.facts['env'] = {}
  474. for k,v in os.environ.iteritems():
  475. self.facts['env'][k] = v
  476.  
  477. class Hardware(Facts):
  478. """
  479. This is a generic Hardware subclass of Facts. This should be further
  480. subclassed to implement per platform. If you subclass this, it
  481. should define:
  482. - memfree_mb
  483. - memtotal_mb
  484. - swapfree_mb
  485. - swaptotal_mb
  486. - processor (a list)
  487. - processor_cores
  488. - processor_count
  489.  
  490. All subclasses MUST define platform.
  491. """
  492. platform = 'Generic'
  493.  
  494. def __new__(cls, *arguments, **keyword):
  495. subclass = cls
  496. for sc in Hardware.__subclasses__():
  497. if sc.platform == platform.system():
  498. subclass = sc
  499. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  500.  
  501. def __init__(self):
  502. Facts.__init__(self)
  503.  
  504. def populate(self):
  505. return self.facts
  506.  
  507. class LinuxHardware(Hardware):
  508. """
  509. Linux-specific subclass of Hardware. Defines memory and CPU facts:
  510. - memfree_mb
  511. - memtotal_mb
  512. - swapfree_mb
  513. - swaptotal_mb
  514. - processor (a list)
  515. - processor_cores
  516. - processor_count
  517.  
  518. In addition, it also defines number of DMI facts and device facts.
  519. """
  520.  
  521. platform = 'Linux'
  522. MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
  523.  
  524. def __init__(self):
  525. Hardware.__init__(self)
  526.  
  527. def populate(self):
  528. self.get_cpu_facts()
  529. self.get_memory_facts()
  530. self.get_dmi_facts()
  531. self.get_device_facts()
  532. try:
  533. self.get_mount_facts()
  534. except TimeoutError:
  535. pass
  536. return self.facts
  537.  
  538. def get_memory_facts(self):
  539. if not os.access("/proc/meminfo", os.R_OK):
  540. return
  541. for line in open("/proc/meminfo").readlines():
  542. data = line.split(":", 1)
  543. key = data[0]
  544. if key in LinuxHardware.MEMORY_FACTS:
  545. val = data[1].strip().split(' ')[0]
  546. self.facts["%s_mb" % key.lower()] = long(val) / 1024
  547.  
  548. def get_cpu_facts(self):
  549. i = 0
  550. physid = 0
  551. coreid = 0
  552. sockets = {}
  553. cores = {}
  554. if not os.access("/proc/cpuinfo", os.R_OK):
  555. return
  556. self.facts['processor'] = []
  557. for line in open("/proc/cpuinfo").readlines():
  558. data = line.split(":", 1)
  559. key = data[0].strip()
  560. # model name is for Intel arch, Processor (mind the uppercase P)
  561. # works for some ARM devices, like the Sheevaplug.
  562. if key == 'model name' or key == 'Processor':
  563. if 'processor' not in self.facts:
  564. self.facts['processor'] = []
  565. self.facts['processor'].append(data[1].strip())
  566. i += 1
  567. elif key == 'physical id':
  568. physid = data[1].strip()
  569. if physid not in sockets:
  570. sockets[physid] = 1
  571. elif key == 'core id':
  572. coreid = data[1].strip()
  573. if coreid not in sockets:
  574. cores[coreid] = 1
  575. elif key == 'cpu cores':
  576. sockets[physid] = int(data[1].strip())
  577. elif key == 'siblings':
  578. cores[coreid] = int(data[1].strip())
  579. self.facts['processor_count'] = sockets and len(sockets) or i
  580. self.facts['processor_cores'] = sockets.values() and sockets.values()[0] or 1
  581. self.facts['processor_threads_per_core'] = ((cores.values() and
  582. cores.values()[0] or 1) / self.facts['processor_cores'])
  583. self.facts['processor_vcpus'] = (self.facts['processor_threads_per_core'] *
  584. self.facts['processor_count'] * self.facts['processor_cores'])
  585.  
  586. def get_dmi_facts(self):
  587. ''' learn dmi facts from system
  588.  
  589. Try /sys first for dmi related facts.
  590. If that is not available, fall back to dmidecode executable '''
  591.  
  592. if os.path.exists('/sys/devices/virtual/dmi/id/product_name'):
  593. # Use kernel DMI info, if available
  594.  
  595. # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
  596. FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
  597. "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
  598. "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
  599. "All In One", "Sub Notebook", "Space-saving", "Lunch Box",
  600. "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
  601. "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
  602. "Rack Mount Chassis", "Sealed-case PC", "Multi-system",
  603. "CompactPCI", "AdvancedTCA", "Blade" ]
  604.  
  605. DMI_DICT = {
  606. 'bios_date': '/sys/devices/virtual/dmi/id/bios_date',
  607. 'bios_version': '/sys/devices/virtual/dmi/id/bios_version',
  608. 'form_factor': '/sys/devices/virtual/dmi/id/chassis_type',
  609. 'product_name': '/sys/devices/virtual/dmi/id/product_name',
  610. 'product_serial': '/sys/devices/virtual/dmi/id/product_serial',
  611. 'product_uuid': '/sys/devices/virtual/dmi/id/product_uuid',
  612. 'product_version': '/sys/devices/virtual/dmi/id/product_version',
  613. 'system_vendor': '/sys/devices/virtual/dmi/id/sys_vendor'
  614. }
  615.  
  616. for (key,path) in DMI_DICT.items():
  617. data = get_file_content(path)
  618. if data is not None:
  619. if key == 'form_factor':
  620. try:
  621. self.facts['form_factor'] = FORM_FACTOR[int(data)]
  622. except IndexError, e:
  623. self.facts['form_factor'] = 'unknown (%s)' % data
  624. else:
  625. self.facts[key] = data
  626. else:
  627. self.facts[key] = 'NA'
  628.  
  629. else:
  630. # Fall back to using dmidecode, if available
  631. dmi_bin = module.get_bin_path('dmidecode')
  632. DMI_DICT = {
  633. 'bios_date': 'bios-release-date',
  634. 'bios_version': 'bios-version',
  635. 'form_factor': 'chassis-type',
  636. 'product_name': 'system-product-name',
  637. 'product_serial': 'system-serial-number',
  638. 'product_uuid': 'system-uuid',
  639. 'product_version': 'system-version',
  640. 'system_vendor': 'system-manufacturer'
  641. }
  642. for (k, v) in DMI_DICT.items():
  643. if dmi_bin is not None:
  644. (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
  645. if rc == 0:
  646. # Strip out commented lines (specific dmidecode output)
  647. thisvalue = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
  648. try:
  649. json.dumps(thisvalue)
  650. except UnicodeDecodeError:
  651. thisvalue = "NA"
  652.  
  653. self.facts[k] = thisvalue
  654. else:
  655. self.facts[k] = 'NA'
  656. else:
  657. self.facts[k] = 'NA'
  658.  
  659. @timeout(10)
  660. def get_mount_facts(self):
  661. self.facts['mounts'] = []
  662. mtab = get_file_content('/etc/mtab', '')
  663. for line in mtab.split('\n'):
  664. if line.startswith('/'):
  665. fields = line.rstrip('\n').split()
  666. if(fields[2] != 'none'):
  667. size_total = None
  668. size_available = None
  669. try:
  670. statvfs_result = os.statvfs(fields[1])
  671. size_total = statvfs_result.f_bsize * statvfs_result.f_blocks
  672. size_available = statvfs_result.f_bsize * (statvfs_result.f_bavail)
  673. except OSError, e:
  674. continue
  675.  
  676. self.facts['mounts'].append(
  677. {'mount': fields[1],
  678. 'device':fields[0],
  679. 'fstype': fields[2],
  680. 'options': fields[3],
  681. # statvfs data
  682. 'size_total': size_total,
  683. 'size_available': size_available,
  684. })
  685.  
  686. def get_device_facts(self):
  687. self.facts['devices'] = {}
  688. lspci = module.get_bin_path('lspci')
  689. if lspci:
  690. rc, pcidata, err = module.run_command([lspci, '-D'])
  691. else:
  692. pcidata = None
  693.  
  694. try:
  695. block_devs = os.listdir("/sys/block")
  696. except OSError:
  697. return
  698.  
  699. for block in block_devs:
  700. virtual = 1
  701. sysfs_no_links = 0
  702. try:
  703. path = os.readlink(os.path.join("/sys/block/", block))
  704. except OSError, e:
  705. if e.errno == errno.EINVAL:
  706. path = block
  707. sysfs_no_links = 1
  708. else:
  709. continue
  710. if "virtual" in path:
  711. continue
  712. sysdir = os.path.join("/sys/block", path)
  713. if sysfs_no_links == 1:
  714. for folder in os.listdir(sysdir):
  715. if "device" in folder:
  716. virtual = 0
  717. break
  718. if virtual:
  719. continue
  720. d = {}
  721. diskname = os.path.basename(sysdir)
  722. for key in ['vendor', 'model']:
  723. d[key] = get_file_content(sysdir + "/device/" + key)
  724.  
  725. for key,test in [ ('removable','/removable'), \
  726. ('support_discard','/queue/discard_granularity'),
  727. ]:
  728. d[key] = get_file_content(sysdir + test)
  729.  
  730. d['partitions'] = {}
  731. for folder in os.listdir(sysdir):
  732. m = re.search("(" + diskname + "\d+)", folder)
  733. if m:
  734. part = {}
  735. partname = m.group(1)
  736. part_sysdir = sysdir + "/" + partname
  737.  
  738. part['start'] = get_file_content(part_sysdir + "/start",0)
  739. part['sectors'] = get_file_content(part_sysdir + "/size",0)
  740. part['sectorsize'] = get_file_content(part_sysdir + "/queue/hw_sector_size",512)
  741. part['size'] = module.pretty_bytes((float(part['sectors']) * float(part['sectorsize'])))
  742. d['partitions'][partname] = part
  743.  
  744. d['rotational'] = get_file_content(sysdir + "/queue/rotational")
  745. d['scheduler_mode'] = ""
  746. scheduler = get_file_content(sysdir + "/queue/scheduler")
  747. if scheduler is not None:
  748. m = re.match(".*?(\[(.*)\])", scheduler)
  749. if m:
  750. d['scheduler_mode'] = m.group(2)
  751.  
  752. d['sectors'] = get_file_content(sysdir + "/size")
  753. if not d['sectors']:
  754. d['sectors'] = 0
  755. d['sectorsize'] = get_file_content(sysdir + "/queue/hw_sector_size")
  756. if not d['sectorsize']:
  757. d['sectorsize'] = 512
  758. d['size'] = module.pretty_bytes(float(d['sectors']) * float(d['sectorsize']))
  759.  
  760. d['host'] = ""
  761.  
  762. # domains are numbered (0 to ffff), bus (0 to ff), slot (0 to 1f), and function (0 to 7).
  763. m = re.match(".+/([a-f0-9]{4}:[a-f0-9]{2}:[0|1][a-f0-9]\.[0-7])/", sysdir)
  764. if m and pcidata:
  765. pciid = m.group(1)
  766. did = re.escape(pciid)
  767. m = re.search("^" + did + "\s(.*)$", pcidata, re.MULTILINE)
  768. d['host'] = m.group(1)
  769.  
  770. d['holders'] = []
  771. if os.path.isdir(sysdir + "/holders"):
  772. for folder in os.listdir(sysdir + "/holders"):
  773. if not folder.startswith("dm-"):
  774. continue
  775. name = get_file_content(sysdir + "/holders/" + folder + "/dm/name")
  776. if name:
  777. d['holders'].append(name)
  778. else:
  779. d['holders'].append(folder)
  780.  
  781. self.facts['devices'][diskname] = d
  782.  
  783.  
  784. class SunOSHardware(Hardware):
  785. """
  786. In addition to the generic memory and cpu facts, this also sets
  787. swap_reserved_mb and swap_allocated_mb that is available from *swap -s*.
  788. """
  789. platform = 'SunOS'
  790.  
  791. def __init__(self):
  792. Hardware.__init__(self)
  793.  
  794. def populate(self):
  795. self.get_cpu_facts()
  796. self.get_memory_facts()
  797. return self.facts
  798.  
  799. def get_cpu_facts(self):
  800. physid = 0
  801. sockets = {}
  802. rc, out, err = module.run_command("/usr/bin/kstat cpu_info")
  803. self.facts['processor'] = []
  804. for line in out.split('\n'):
  805. if len(line) < 1:
  806. continue
  807. data = line.split(None, 1)
  808. key = data[0].strip()
  809. # "brand" works on Solaris 10 & 11. "implementation" for Solaris 9.
  810. if key == 'module:':
  811. brand = ''
  812. elif key == 'brand':
  813. brand = data[1].strip()
  814. elif key == 'clock_MHz':
  815. clock_mhz = data[1].strip()
  816. elif key == 'implementation':
  817. processor = brand or data[1].strip()
  818. # Add clock speed to description for SPARC CPU
  819. if self.facts['machine'] != 'i86pc':
  820. processor += " @ " + clock_mhz + "MHz"
  821. if 'processor' not in self.facts:
  822. self.facts['processor'] = []
  823. self.facts['processor'].append(processor)
  824. elif key == 'chip_id':
  825. physid = data[1].strip()
  826. if physid not in sockets:
  827. sockets[physid] = 1
  828. else:
  829. sockets[physid] += 1
  830. # Counting cores on Solaris can be complicated.
  831. # https://blogs.oracle.com/mandalika/entry/solaris_show_me_the_cpu
  832. # Treat 'processor_count' as physical sockets and 'processor_cores' as
  833. # virtual CPUs visisble to Solaris. Not a true count of cores for modern SPARC as
  834. # these processors have: sockets -> cores -> threads/virtual CPU.
  835. if len(sockets) > 0:
  836. self.facts['processor_count'] = len(sockets)
  837. self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
  838. else:
  839. self.facts['processor_cores'] = 'NA'
  840. self.facts['processor_count'] = len(self.facts['processor'])
  841.  
  842. def get_memory_facts(self):
  843. rc, out, err = module.run_command(["/usr/sbin/prtconf"])
  844. for line in out.split('\n'):
  845. if 'Memory size' in line:
  846. self.facts['memtotal_mb'] = line.split()[2]
  847. rc, out, err = module.run_command("/usr/sbin/swap -s")
  848. allocated = long(out.split()[1][:-1])
  849. reserved = long(out.split()[5][:-1])
  850. used = long(out.split()[8][:-1])
  851. free = long(out.split()[10][:-1])
  852. self.facts['swapfree_mb'] = free / 1024
  853. self.facts['swaptotal_mb'] = (free + used) / 1024
  854. self.facts['swap_allocated_mb'] = allocated / 1024
  855. self.facts['swap_reserved_mb'] = reserved / 1024
  856.  
  857. class OpenBSDHardware(Hardware):
  858. """
  859. OpenBSD-specific subclass of Hardware. Defines memory, CPU and device facts:
  860. - memfree_mb
  861. - memtotal_mb
  862. - swapfree_mb
  863. - swaptotal_mb
  864. - processor (a list)
  865. - processor_cores
  866. - processor_count
  867. - processor_speed
  868. - devices
  869. """
  870. platform = 'OpenBSD'
  871. DMESG_BOOT = '/var/run/dmesg.boot'
  872.  
  873. def __init__(self):
  874. Hardware.__init__(self)
  875.  
  876. def populate(self):
  877. self.sysctl = self.get_sysctl()
  878. self.get_memory_facts()
  879. self.get_processor_facts()
  880. self.get_device_facts()
  881. return self.facts
  882.  
  883. def get_sysctl(self):
  884. rc, out, err = module.run_command(["/sbin/sysctl", "hw"])
  885. if rc != 0:
  886. return dict()
  887. sysctl = dict()
  888. for line in out.splitlines():
  889. (key, value) = line.split('=')
  890. sysctl[key] = value.strip()
  891. return sysctl
  892.  
  893. def get_memory_facts(self):
  894. # Get free memory. vmstat output looks like:
  895. # procs memory page disks traps cpu
  896. # r b w avm fre flt re pi po fr sr wd0 fd0 int sys cs us sy id
  897. # 0 0 0 47512 28160 51 0 0 0 0 0 1 0 116 89 17 0 1 99
  898. rc, out, err = module.run_command("/usr/bin/vmstat")
  899. if rc == 0:
  900. self.facts['memfree_mb'] = long(out.splitlines()[-1].split()[4]) / 1024
  901. self.facts['memtotal_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024
  902.  
  903. # Get swapctl info. swapctl output looks like:
  904. # total: 69268 1K-blocks allocated, 0 used, 69268 available
  905. # And for older OpenBSD:
  906. # total: 69268k bytes allocated = 0k used, 69268k available
  907. rc, out, err = module.run_command("/sbin/swapctl -sk")
  908. if rc == 0:
  909. data = out.split()
  910. self.facts['swapfree_mb'] = long(data[-2].translate(None, "kmg")) / 1024
  911. self.facts['swaptotal_mb'] = long(data[1].translate(None, "kmg")) / 1024
  912.  
  913. def get_processor_facts(self):
  914. processor = []
  915. dmesg_boot = get_file_content(OpenBSDHardware.DMESG_BOOT)
  916. if not dmesg_boot:
  917. rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
  918. i = 0
  919. for line in dmesg_boot.splitlines():
  920. if line.split(' ', 1)[0] == 'cpu%i:' % i:
  921. processor.append(line.split(' ', 1)[1])
  922. i = i + 1
  923. processor_count = i
  924. self.facts['processor'] = processor
  925. self.facts['processor_count'] = processor_count
  926. # I found no way to figure out the number of Cores per CPU in OpenBSD
  927. self.facts['processor_cores'] = 'NA'
  928.  
  929. def get_device_facts(self):
  930. devices = []
  931. devices.extend(self.sysctl['hw.disknames'].split(','))
  932. self.facts['devices'] = devices
  933.  
  934. class FreeBSDHardware(Hardware):
  935. """
  936. FreeBSD-specific subclass of Hardware. Defines memory and CPU facts:
  937. - memfree_mb
  938. - memtotal_mb
  939. - swapfree_mb
  940. - swaptotal_mb
  941. - processor (a list)
  942. - processor_cores
  943. - processor_count
  944. - devices
  945. """
  946. platform = 'FreeBSD'
  947. DMESG_BOOT = '/var/run/dmesg.boot'
  948.  
  949. def __init__(self):
  950. Hardware.__init__(self)
  951.  
  952. def populate(self):
  953. self.get_cpu_facts()
  954. self.get_memory_facts()
  955. self.get_dmi_facts()
  956. self.get_device_facts()
  957. try:
  958. self.get_mount_facts()
  959. except TimeoutError:
  960. pass
  961. return self.facts
  962.  
  963. def get_cpu_facts(self):
  964. self.facts['processor'] = []
  965. rc, out, err = module.run_command("/sbin/sysctl -n hw.ncpu")
  966. self.facts['processor_count'] = out.strip()
  967.  
  968. dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT)
  969. if not dmesg_boot:
  970. rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
  971. for line in dmesg_boot.split('\n'):
  972. if 'CPU:' in line:
  973. cpu = re.sub(r'CPU:\s+', r"", line)
  974. self.facts['processor'].append(cpu.strip())
  975. if 'Logical CPUs per core' in line:
  976. self.facts['processor_cores'] = line.split()[4]
  977.  
  978.  
  979. def get_memory_facts(self):
  980. rc, out, err = module.run_command("/sbin/sysctl vm.stats")
  981. for line in out.split('\n'):
  982. data = line.split()
  983. if 'vm.stats.vm.v_page_size' in line:
  984. pagesize = long(data[1])
  985. if 'vm.stats.vm.v_page_count' in line:
  986. pagecount = long(data[1])
  987. if 'vm.stats.vm.v_free_count' in line:
  988. freecount = long(data[1])
  989. self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
  990. self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
  991. # Get swapinfo. swapinfo output looks like:
  992. # Device 1M-blocks Used Avail Capacity
  993. # /dev/ada0p3 314368 0 314368 0%
  994. #
  995. rc, out, err = module.run_command("/usr/sbin/swapinfo -m")
  996. lines = out.split('\n')
  997. if len(lines[-1]) == 0:
  998. lines.pop()
  999. data = lines[-1].split()
  1000. self.facts['swaptotal_mb'] = data[1]
  1001. self.facts['swapfree_mb'] = data[3]
  1002.  
  1003. @timeout(10)
  1004. def get_mount_facts(self):
  1005. self.facts['mounts'] = []
  1006. fstab = get_file_content('/etc/fstab')
  1007. if fstab:
  1008. for line in fstab.split('\n'):
  1009. if line.startswith('#') or line.strip() == '':
  1010. continue
  1011. fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
  1012. self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
  1013.  
  1014. def get_device_facts(self):
  1015. sysdir = '/dev'
  1016. self.facts['devices'] = {}
  1017. drives = re.compile('(ada?\d+|da\d+|a?cd\d+)') #TODO: rc, disks, err = module.run_command("/sbin/sysctl kern.disks")
  1018. slices = re.compile('(ada?\d+s\d+\w*|da\d+s\d+\w*)')
  1019. if os.path.isdir(sysdir):
  1020. dirlist = sorted(os.listdir(sysdir))
  1021. for device in dirlist:
  1022. d = drives.match(device)
  1023. if d:
  1024. self.facts['devices'][d.group(1)] = []
  1025. s = slices.match(device)
  1026. if s:
  1027. self.facts['devices'][d.group(1)].append(s.group(1))
  1028.  
  1029. def get_dmi_facts(self):
  1030. ''' learn dmi facts from system
  1031.  
  1032. Use dmidecode executable if available'''
  1033.  
  1034. # Fall back to using dmidecode, if available
  1035. dmi_bin = module.get_bin_path('dmidecode')
  1036. DMI_DICT = dict(
  1037. bios_date='bios-release-date',
  1038. bios_version='bios-version',
  1039. form_factor='chassis-type',
  1040. product_name='system-product-name',
  1041. product_serial='system-serial-number',
  1042. product_uuid='system-uuid',
  1043. product_version='system-version',
  1044. system_vendor='system-manufacturer'
  1045. )
  1046. for (k, v) in DMI_DICT.items():
  1047. if dmi_bin is not None:
  1048. (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
  1049. if rc == 0:
  1050. # Strip out commented lines (specific dmidecode output)
  1051. self.facts[k] = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
  1052. try:
  1053. json.dumps(self.facts[k])
  1054. except UnicodeDecodeError:
  1055. self.facts[k] = 'NA'
  1056. else:
  1057. self.facts[k] = 'NA'
  1058. else:
  1059. self.facts[k] = 'NA'
  1060.  
  1061.  
  1062. class NetBSDHardware(Hardware):
  1063. """
  1064. NetBSD-specific subclass of Hardware. Defines memory and CPU facts:
  1065. - memfree_mb
  1066. - memtotal_mb
  1067. - swapfree_mb
  1068. - swaptotal_mb
  1069. - processor (a list)
  1070. - processor_cores
  1071. - processor_count
  1072. - devices
  1073. """
  1074. platform = 'NetBSD'
  1075. MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
  1076.  
  1077. def __init__(self):
  1078. Hardware.__init__(self)
  1079.  
  1080. def populate(self):
  1081. self.get_cpu_facts()
  1082. self.get_memory_facts()
  1083. try:
  1084. self.get_mount_facts()
  1085. except TimeoutError:
  1086. pass
  1087. return self.facts
  1088.  
  1089. def get_cpu_facts(self):
  1090.  
  1091. i = 0
  1092. physid = 0
  1093. sockets = {}
  1094. if not os.access("/proc/cpuinfo", os.R_OK):
  1095. return
  1096. self.facts['processor'] = []
  1097. for line in open("/proc/cpuinfo").readlines():
  1098. data = line.split(":", 1)
  1099. key = data[0].strip()
  1100. # model name is for Intel arch, Processor (mind the uppercase P)
  1101. # works for some ARM devices, like the Sheevaplug.
  1102. if key == 'model name' or key == 'Processor':
  1103. if 'processor' not in self.facts:
  1104. self.facts['processor'] = []
  1105. self.facts['processor'].append(data[1].strip())
  1106. i += 1
  1107. elif key == 'physical id':
  1108. physid = data[1].strip()
  1109. if physid not in sockets:
  1110. sockets[physid] = 1
  1111. elif key == 'cpu cores':
  1112. sockets[physid] = int(data[1].strip())
  1113. if len(sockets) > 0:
  1114. self.facts['processor_count'] = len(sockets)
  1115. self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
  1116. else:
  1117. self.facts['processor_count'] = i
  1118. self.facts['processor_cores'] = 'NA'
  1119.  
  1120. def get_memory_facts(self):
  1121. if not os.access("/proc/meminfo", os.R_OK):
  1122. return
  1123. for line in open("/proc/meminfo").readlines():
  1124. data = line.split(":", 1)
  1125. key = data[0]
  1126. if key in NetBSDHardware.MEMORY_FACTS:
  1127. val = data[1].strip().split(' ')[0]
  1128. self.facts["%s_mb" % key.lower()] = long(val) / 1024
  1129.  
  1130. @timeout(10)
  1131. def get_mount_facts(self):
  1132. self.facts['mounts'] = []
  1133. fstab = get_file_content('/etc/fstab')
  1134. if fstab:
  1135. for line in fstab.split('\n'):
  1136. if line.startswith('#') or line.strip() == '':
  1137. continue
  1138. fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
  1139. self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
  1140.  
  1141. class AIX(Hardware):
  1142. """
  1143. AIX-specific subclass of Hardware. Defines memory and CPU facts:
  1144. - memfree_mb
  1145. - memtotal_mb
  1146. - swapfree_mb
  1147. - swaptotal_mb
  1148. - processor (a list)
  1149. - processor_cores
  1150. - processor_count
  1151. """
  1152. platform = 'AIX'
  1153.  
  1154. def __init__(self):
  1155. Hardware.__init__(self)
  1156.  
  1157. def populate(self):
  1158. self.get_cpu_facts()
  1159. self.get_memory_facts()
  1160. self.get_dmi_facts()
  1161. return self.facts
  1162.  
  1163. def get_cpu_facts(self):
  1164. self.facts['processor'] = []
  1165.  
  1166.  
  1167. rc, out, err = module.run_command("/usr/sbin/lsdev -Cc processor")
  1168. if out:
  1169. i = 0
  1170. for line in out.split('\n'):
  1171.  
  1172. if 'Available' in line:
  1173. if i == 0:
  1174. data = line.split(' ')
  1175. cpudev = data[0]
  1176.  
  1177. i += 1
  1178. self.facts['processor_count'] = int(i)
  1179.  
  1180. rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a type")
  1181.  
  1182. data = out.split(' ')
  1183. self.facts['processor'] = data[1]
  1184.  
  1185. rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a smt_threads")
  1186.  
  1187. data = out.split(' ')
  1188. self.facts['processor_cores'] = int(data[1])
  1189.  
  1190. def get_memory_facts(self):
  1191. pagesize = 4096
  1192. rc, out, err = module.run_command("/usr/bin/vmstat -v")
  1193. for line in out.split('\n'):
  1194. data = line.split()
  1195. if 'memory pages' in line:
  1196. pagecount = long(data[0])
  1197. if 'free pages' in line:
  1198. freecount = long(data[0])
  1199. self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
  1200. self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
  1201. # Get swapinfo. swapinfo output looks like:
  1202. # Device 1M-blocks Used Avail Capacity
  1203. # /dev/ada0p3 314368 0 314368 0%
  1204. #
  1205. rc, out, err = module.run_command("/usr/sbin/lsps -s")
  1206. if out:
  1207. lines = out.split('\n')
  1208. data = lines[1].split()
  1209. swaptotal_mb = long(data[0].rstrip('MB'))
  1210. percused = int(data[1].rstrip('%'))
  1211. self.facts['swaptotal_mb'] = swaptotal_mb
  1212. self.facts['swapfree_mb'] = long(swaptotal_mb * ( 100 - percused ) / 100)
  1213.  
  1214. def get_dmi_facts(self):
  1215. rc, out, err = module.run_command("/usr/sbin/lsattr -El sys0 -a fwversion")
  1216. data = out.split()
  1217. self.facts['firmware_version'] = data[1].strip('IBM,')
  1218.  
  1219. class HPUX(Hardware):
  1220. """
  1221. HP-UX-specifig subclass of Hardware. Defines memory and CPU facts:
  1222. - memfree_mb
  1223. - memtotal_mb
  1224. - swapfree_mb
  1225. - swaptotal_mb
  1226. - processor
  1227. - processor_cores
  1228. - processor_count
  1229. - model
  1230. - firmware
  1231. """
  1232.  
  1233. platform = 'HP-UX'
  1234.  
  1235. def __init__(self):
  1236. Hardware.__init__(self)
  1237.  
  1238. def populate(self):
  1239. self.get_cpu_facts()
  1240. self.get_memory_facts()
  1241. self.get_hw_facts()
  1242. return self.facts
  1243.  
  1244. def get_cpu_facts(self):
  1245. if self.facts['architecture'] == '9000/800':
  1246. rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True)
  1247. self.facts['processor_count'] = int(out.strip())
  1248. #Working with machinfo mess
  1249. elif self.facts['architecture'] == 'ia64':
  1250. if self.facts['distribution_version'] == "B.11.23":
  1251. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'Number of CPUs'", use_unsafe_shell=True)
  1252. self.facts['processor_count'] = int(out.strip().split('=')[1])
  1253. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'processor family'", use_unsafe_shell=True)
  1254. self.facts['processor'] = re.search('.*(Intel.*)', out).groups()[0].strip()
  1255. rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True)
  1256. self.facts['processor_cores'] = int(out.strip())
  1257. if self.facts['distribution_version'] == "B.11.31":
  1258. #if machinfo return cores strings release B.11.31 > 1204
  1259. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep core | wc -l", use_unsafe_shell=True)
  1260. if out.strip()== '0':
  1261. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True)
  1262. self.facts['processor_count'] = int(out.strip().split(" ")[0])
  1263. #If hyperthreading is active divide cores by 2
  1264. rc, out, err = module.run_command("/usr/sbin/psrset | grep LCPU", use_unsafe_shell=True)
  1265. data = re.sub(' +',' ',out).strip().split(' ')
  1266. if len(data) == 1:
  1267. hyperthreading = 'OFF'
  1268. else:
  1269. hyperthreading = data[1]
  1270. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep logical", use_unsafe_shell=True)
  1271. data = out.strip().split(" ")
  1272. if hyperthreading == 'ON':
  1273. self.facts['processor_cores'] = int(data[0])/2
  1274. else:
  1275. if len(data) == 1:
  1276. self.facts['processor_cores'] = self.facts['processor_count']
  1277. else:
  1278. self.facts['processor_cores'] = int(data[0])
  1279. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel |cut -d' ' -f4-", use_unsafe_shell=True)
  1280. self.facts['processor'] = out.strip()
  1281. else:
  1282. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | egrep 'socket[s]?$' | tail -1", use_unsafe_shell=True)
  1283. self.facts['processor_count'] = int(out.strip().split(" ")[0])
  1284. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep -e '[0-9] core' | tail -1", use_unsafe_shell=True)
  1285. self.facts['processor_cores'] = int(out.strip().split(" ")[0])
  1286. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True)
  1287. self.facts['processor'] = out.strip()
  1288.  
  1289. def get_memory_facts(self):
  1290. pagesize = 4096
  1291. rc, out, err = module.run_command("/usr/bin/vmstat | tail -1", use_unsafe_shell=True)
  1292. data = int(re.sub(' +',' ',out).split(' ')[5].strip())
  1293. self.facts['memfree_mb'] = pagesize * data / 1024 / 1024
  1294. if self.facts['architecture'] == '9000/800':
  1295. rc, out, err = module.run_command("grep Physical /var/adm/syslog/syslog.log")
  1296. data = re.search('.*Physical: ([0-9]*) Kbytes.*',out).groups()[0].strip()
  1297. self.facts['memtotal_mb'] = int(data) / 1024
  1298. else:
  1299. rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Memory", use_unsafe_shell=True)
  1300. data = re.search('Memory[\ :=]*([0-9]*).*MB.*',out).groups()[0].strip()
  1301. self.facts['memtotal_mb'] = int(data)
  1302. rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f -q")
  1303. self.facts['swaptotal_mb'] = int(out.strip())
  1304. rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f | egrep '^dev|^fs'", use_unsafe_shell=True)
  1305. swap = 0
  1306. for line in out.strip().split('\n'):
  1307. swap += int(re.sub(' +',' ',line).split(' ')[3].strip())
  1308. self.facts['swapfree_mb'] = swap
  1309.  
  1310. def get_hw_facts(self):
  1311. rc, out, err = module.run_command("model")
  1312. self.facts['model'] = out.strip()
  1313. if self.facts['architecture'] == 'ia64':
  1314. rc, out, err = module.run_command("/usr/contrib/bin/machinfo |grep -i 'Firmware revision' | grep -v BMC", use_unsafe_shell=True)
  1315. self.facts['firmware_version'] = out.split(':')[1].strip()
  1316.  
  1317.  
  1318. class Darwin(Hardware):
  1319. """
  1320. Darwin-specific subclass of Hardware. Defines memory and CPU facts:
  1321. - processor
  1322. - processor_cores
  1323. - memtotal_mb
  1324. - memfree_mb
  1325. - model
  1326. - osversion
  1327. - osrevision
  1328. """
  1329. platform = 'Darwin'
  1330.  
  1331. def __init__(self):
  1332. Hardware.__init__(self)
  1333.  
  1334. def populate(self):
  1335. self.sysctl = self.get_sysctl()
  1336. self.get_mac_facts()
  1337. self.get_cpu_facts()
  1338. self.get_memory_facts()
  1339. return self.facts
  1340.  
  1341. def get_sysctl(self):
  1342. rc, out, err = module.run_command(["/usr/sbin/sysctl", "hw", "machdep", "kern"])
  1343. if rc != 0:
  1344. return dict()
  1345. sysctl = dict()
  1346. for line in out.splitlines():
  1347. if line.rstrip("\n"):
  1348. (key, value) = re.split(' = |: ', line, maxsplit=1)
  1349. sysctl[key] = value.strip()
  1350. return sysctl
  1351.  
  1352. def get_system_profile(self):
  1353. rc, out, err = module.run_command(["/usr/sbin/system_profiler", "SPHardwareDataType"])
  1354. if rc != 0:
  1355. return dict()
  1356. system_profile = dict()
  1357. for line in out.splitlines():
  1358. if ': ' in line:
  1359. (key, value) = line.split(': ', 1)
  1360. system_profile[key.strip()] = ' '.join(value.strip().split())
  1361. return system_profile
  1362.  
  1363. def get_mac_facts(self):
  1364. self.facts['model'] = self.sysctl['hw.model']
  1365. self.facts['osversion'] = self.sysctl['kern.osversion']
  1366. self.facts['osrevision'] = self.sysctl['kern.osrevision']
  1367.  
  1368. def get_cpu_facts(self):
  1369. if 'machdep.cpu.brand_string' in self.sysctl: # Intel
  1370. self.facts['processor'] = self.sysctl['machdep.cpu.brand_string']
  1371. self.facts['processor_cores'] = self.sysctl['machdep.cpu.core_count']
  1372. else: # PowerPC
  1373. system_profile = self.get_system_profile()
  1374. self.facts['processor'] = '%s @ %s' % (system_profile['Processor Name'], system_profile['Processor Speed'])
  1375. self.facts['processor_cores'] = self.sysctl['hw.physicalcpu']
  1376.  
  1377. def get_memory_facts(self):
  1378. self.facts['memtotal_mb'] = long(self.sysctl['hw.memsize']) / 1024 / 1024
  1379. self.facts['memfree_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024
  1380.  
  1381. class Network(Facts):
  1382. """
  1383. This is a generic Network subclass of Facts. This should be further
  1384. subclassed to implement per platform. If you subclass this,
  1385. you must define:
  1386. - interfaces (a list of interface names)
  1387. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1388.  
  1389. All subclasses MUST define platform.
  1390. """
  1391. platform = 'Generic'
  1392.  
  1393. IPV6_SCOPE = { '0' : 'global',
  1394. '10' : 'host',
  1395. '20' : 'link',
  1396. '40' : 'admin',
  1397. '50' : 'site',
  1398. '80' : 'organization' }
  1399.  
  1400. def __new__(cls, *arguments, **keyword):
  1401. subclass = cls
  1402. for sc in Network.__subclasses__():
  1403. if sc.platform == platform.system():
  1404. subclass = sc
  1405. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  1406.  
  1407. def __init__(self, module):
  1408. self.module = module
  1409. Facts.__init__(self)
  1410.  
  1411. def populate(self):
  1412. return self.facts
  1413.  
  1414. class LinuxNetwork(Network):
  1415. """
  1416. This is a Linux-specific subclass of Network. It defines
  1417. - interfaces (a list of interface names)
  1418. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1419. - all_ipv4_addresses and all_ipv6_addresses: lists of all configured addresses.
  1420. - ipv4_address and ipv6_address: the first non-local address for each family.
  1421. """
  1422. platform = 'Linux'
  1423.  
  1424. def __init__(self, module):
  1425. Network.__init__(self, module)
  1426.  
  1427. def populate(self):
  1428. ip_path = self.module.get_bin_path('ip')
  1429. if ip_path is None:
  1430. return self.facts
  1431. default_ipv4, default_ipv6 = self.get_default_interfaces(ip_path)
  1432. interfaces, ips = self.get_interfaces_info(ip_path, default_ipv4, default_ipv6)
  1433. self.facts['interfaces'] = interfaces.keys()
  1434. for iface in interfaces:
  1435. self.facts[iface] = interfaces[iface]
  1436. self.facts['default_ipv4'] = default_ipv4
  1437. self.facts['default_ipv6'] = default_ipv6
  1438. self.facts['all_ipv4_addresses'] = ips['all_ipv4_addresses']
  1439. self.facts['all_ipv6_addresses'] = ips['all_ipv6_addresses']
  1440. return self.facts
  1441.  
  1442. def get_default_interfaces(self, ip_path):
  1443. # Use the commands:
  1444. # ip -4 route get 8.8.8.8 -> Google public DNS
  1445. # ip -6 route get 2404:6800:400a:800::1012 -> ipv6.google.com
  1446. # to find out the default outgoing interface, address, and gateway
  1447. command = dict(
  1448. v4 = [ip_path, '-4', 'route', 'get', '8.8.8.8'],
  1449. v6 = [ip_path, '-6', 'route', 'get', '2404:6800:400a:800::1012']
  1450. )
  1451. interface = dict(v4 = {}, v6 = {})
  1452. for v in 'v4', 'v6':
  1453. if v == 'v6' and self.facts['os_family'] == 'RedHat' \
  1454. and self.facts['distribution_version'].startswith('4.'):
  1455. continue
  1456. if v == 'v6' and not socket.has_ipv6:
  1457. continue
  1458. rc, out, err = module.run_command(command[v])
  1459. if not out:
  1460. # v6 routing may result in
  1461. # RTNETLINK answers: Invalid argument
  1462. continue
  1463. words = out.split('\n')[0].split()
  1464. # A valid output starts with the queried address on the first line
  1465. if len(words) > 0 and words[0] == command[v][-1]:
  1466. for i in range(len(words) - 1):
  1467. if words[i] == 'dev':
  1468. interface[v]['interface'] = words[i+1]
  1469. elif words[i] == 'src':
  1470. interface[v]['address'] = words[i+1]
  1471. elif words[i] == 'via' and words[i+1] != command[v][-1]:
  1472. interface[v]['gateway'] = words[i+1]
  1473. return interface['v4'], interface['v6']
  1474.  
  1475. def get_interfaces_info(self, ip_path, default_ipv4, default_ipv6):
  1476. interfaces = {}
  1477. ips = dict(
  1478. all_ipv4_addresses = [],
  1479. all_ipv6_addresses = [],
  1480. )
  1481.  
  1482. for path in glob.glob('/sys/class/net/*'):
  1483. if not os.path.isdir(path):
  1484. continue
  1485. device = os.path.basename(path)
  1486. interfaces[device] = { 'device': device }
  1487. if os.path.exists(os.path.join(path, 'address')):
  1488. macaddress = open(os.path.join(path, 'address')).read().strip()
  1489. if macaddress and macaddress != '00:00:00:00:00:00':
  1490. interfaces[device]['macaddress'] = macaddress
  1491. if os.path.exists(os.path.join(path, 'mtu')):
  1492. interfaces[device]['mtu'] = int(open(os.path.join(path, 'mtu')).read().strip())
  1493. if os.path.exists(os.path.join(path, 'operstate')):
  1494. interfaces[device]['active'] = open(os.path.join(path, 'operstate')).read().strip() != 'down'
  1495. # if os.path.exists(os.path.join(path, 'carrier')):
  1496. # interfaces[device]['link'] = open(os.path.join(path, 'carrier')).read().strip() == '1'
  1497. if os.path.exists(os.path.join(path, 'device','driver', 'module')):
  1498. interfaces[device]['module'] = os.path.basename(os.path.realpath(os.path.join(path, 'device', 'driver', 'module')))
  1499. if os.path.exists(os.path.join(path, 'type')):
  1500. type = open(os.path.join(path, 'type')).read().strip()
  1501. if type == '1':
  1502. interfaces[device]['type'] = 'ether'
  1503. elif type == '512':
  1504. interfaces[device]['type'] = 'ppp'
  1505. elif type == '772':
  1506. interfaces[device]['type'] = 'loopback'
  1507. if os.path.exists(os.path.join(path, 'bridge')):
  1508. interfaces[device]['type'] = 'bridge'
  1509. interfaces[device]['interfaces'] = [ os.path.basename(b) for b in glob.glob(os.path.join(path, 'brif', '*')) ]
  1510. if os.path.exists(os.path.join(path, 'bridge', 'bridge_id')):
  1511. interfaces[device]['id'] = open(os.path.join(path, 'bridge', 'bridge_id')).read().strip()
  1512. if os.path.exists(os.path.join(path, 'bridge', 'stp_state')):
  1513. interfaces[device]['stp'] = open(os.path.join(path, 'bridge', 'stp_state')).read().strip() == '1'
  1514. if os.path.exists(os.path.join(path, 'bonding')):
  1515. interfaces[device]['type'] = 'bonding'
  1516. interfaces[device]['slaves'] = open(os.path.join(path, 'bonding', 'slaves')).read().split()
  1517. interfaces[device]['mode'] = open(os.path.join(path, 'bonding', 'mode')).read().split()[0]
  1518. interfaces[device]['miimon'] = open(os.path.join(path, 'bonding', 'miimon')).read().split()[0]
  1519. interfaces[device]['lacp_rate'] = open(os.path.join(path, 'bonding', 'lacp_rate')).read().split()[0]
  1520. primary = open(os.path.join(path, 'bonding', 'primary')).read()
  1521. if primary:
  1522. interfaces[device]['primary'] = primary
  1523. path = os.path.join(path, 'bonding', 'all_slaves_active')
  1524. if os.path.exists(path):
  1525. interfaces[device]['all_slaves_active'] = open(path).read() == '1'
  1526.  
  1527. # Check whether a interface is in promiscuous mode
  1528. if os.path.exists(os.path.join(path,'flags')):
  1529. promisc_mode = False
  1530. # The second byte indicates whether the interface is in promiscuous mode.
  1531. # 1 = promisc
  1532. # 0 = no promisc
  1533. data = int(open(os.path.join(path, 'flags')).read().strip(),16)
  1534. promisc_mode = (data & 0x0100 > 0)
  1535. interfaces[device]['promisc'] = promisc_mode
  1536.  
  1537. def parse_ip_output(output, secondary=False):
  1538. for line in output.split('\n'):
  1539. if not line:
  1540. continue
  1541. words = line.split()
  1542. if words[0] == 'inet':
  1543. if '/' in words[1]:
  1544. address, netmask_length = words[1].split('/')
  1545. else:
  1546. # pointopoint interfaces do not have a prefix
  1547. address = words[1]
  1548. netmask_length = "32"
  1549. address_bin = struct.unpack('!L', socket.inet_aton(address))[0]
  1550. netmask_bin = (1<<32) - (1<<32>>int(netmask_length))
  1551. netmask = socket.inet_ntoa(struct.pack('!L', netmask_bin))
  1552. network = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin))
  1553. iface = words[-1]
  1554. if iface != device:
  1555. interfaces[iface] = {}
  1556. if not secondary or "ipv4" not in interfaces[iface]:
  1557. interfaces[iface]['ipv4'] = {'address': address,
  1558. 'netmask': netmask,
  1559. 'network': network}
  1560. else:
  1561. if "ipv4_secondaries" not in interfaces[iface]:
  1562. interfaces[iface]["ipv4_secondaries"] = []
  1563. interfaces[iface]["ipv4_secondaries"].append({
  1564. 'address': address,
  1565. 'netmask': netmask,
  1566. 'network': network,
  1567. })
  1568.  
  1569. # add this secondary IP to the main device
  1570. if secondary:
  1571. if "ipv4_secondaries" not in interfaces[device]:
  1572. interfaces[device]["ipv4_secondaries"] = []
  1573. interfaces[device]["ipv4_secondaries"].append({
  1574. 'address': address,
  1575. 'netmask': netmask,
  1576. 'network': network,
  1577. })
  1578.  
  1579. # If this is the default address, update default_ipv4
  1580. if 'address' in default_ipv4 and default_ipv4['address'] == address:
  1581. default_ipv4['netmask'] = netmask
  1582. default_ipv4['network'] = network
  1583. default_ipv4['macaddress'] = macaddress
  1584. default_ipv4['mtu'] = interfaces[device]['mtu']
  1585. default_ipv4['type'] = interfaces[device].get("type", "unknown")
  1586. default_ipv4['alias'] = words[-1]
  1587. if not address.startswith('127.'):
  1588. ips['all_ipv4_addresses'].append(address)
  1589. elif words[0] == 'inet6':
  1590. address, prefix = words[1].split('/')
  1591. scope = words[3]
  1592. if 'ipv6' not in interfaces[device]:
  1593. interfaces[device]['ipv6'] = []
  1594. interfaces[device]['ipv6'].append({
  1595. 'address' : address,
  1596. 'prefix' : prefix,
  1597. 'scope' : scope
  1598. })
  1599. # If this is the default address, update default_ipv6
  1600. if 'address' in default_ipv6 and default_ipv6['address'] == address:
  1601. default_ipv6['prefix'] = prefix
  1602. default_ipv6['scope'] = scope
  1603. default_ipv6['macaddress'] = macaddress
  1604. default_ipv6['mtu'] = interfaces[device]['mtu']
  1605. default_ipv6['type'] = interfaces[device].get("type", "unknown")
  1606. if not address == '::1':
  1607. ips['all_ipv6_addresses'].append(address)
  1608.  
  1609. ip_path = module.get_bin_path("ip")
  1610.  
  1611. args = [ip_path, 'addr', 'show', 'primary', device]
  1612. rc, stdout, stderr = self.module.run_command(args)
  1613. primary_data = stdout
  1614.  
  1615. args = [ip_path, 'addr', 'show', 'secondary', device]
  1616. rc, stdout, stderr = self.module.run_command(args)
  1617. secondary_data = stdout
  1618.  
  1619. parse_ip_output(primary_data)
  1620. parse_ip_output(secondary_data, secondary=True)
  1621.  
  1622. # replace : by _ in interface name since they are hard to use in template
  1623. new_interfaces = {}
  1624. for i in interfaces:
  1625. if ':' in i:
  1626. new_interfaces[i.replace(':','_')] = interfaces[i]
  1627. else:
  1628. new_interfaces[i] = interfaces[i]
  1629. return new_interfaces, ips
  1630.  
  1631. class GenericBsdIfconfigNetwork(Network):
  1632. """
  1633. This is a generic BSD subclass of Network using the ifconfig command.
  1634. It defines
  1635. - interfaces (a list of interface names)
  1636. - interface_<name> dictionary of ipv4, ipv6, and mac address information.
  1637. - all_ipv4_addresses and all_ipv6_addresses: lists of all configured addresses.
  1638. It currently does not define
  1639. - default_ipv4 and default_ipv6
  1640. - type, mtu and network on interfaces
  1641. """
  1642. platform = 'Generic_BSD_Ifconfig'
  1643.  
  1644. def __init__(self, module):
  1645. Network.__init__(self, module)
  1646.  
  1647. def populate(self):
  1648.  
  1649. ifconfig_path = module.get_bin_path('ifconfig')
  1650.  
  1651. if ifconfig_path is None:
  1652. return self.facts
  1653. route_path = module.get_bin_path('route')
  1654.  
  1655. if route_path is None:
  1656. return self.facts
  1657.  
  1658. default_ipv4, default_ipv6 = self.get_default_interfaces(route_path)
  1659. interfaces, ips = self.get_interfaces_info(ifconfig_path)
  1660. self.merge_default_interface(default_ipv4, interfaces, 'ipv4')
  1661. self.merge_default_interface(default_ipv6, interfaces, 'ipv6')
  1662. self.facts['interfaces'] = interfaces.keys()
  1663.  
  1664. for iface in interfaces:
  1665. self.facts[iface] = interfaces[iface]
  1666.  
  1667. self.facts['default_ipv4'] = default_ipv4
  1668. self.facts['default_ipv6'] = default_ipv6
  1669. self.facts['all_ipv4_addresses'] = ips['all_ipv4_addresses']
  1670. self.facts['all_ipv6_addresses'] = ips['all_ipv6_addresses']
  1671.  
  1672. return self.facts
  1673.  
  1674. def get_default_interfaces(self, route_path):
  1675.  
  1676. # Use the commands:
  1677. # route -n get 8.8.8.8 -> Google public DNS
  1678. # route -n get -inet6 2404:6800:400a:800::1012 -> ipv6.google.com
  1679. # to find out the default outgoing interface, address, and gateway
  1680.  
  1681. command = dict(
  1682. v4 = [route_path, '-n', 'get', '8.8.8.8'],
  1683. v6 = [route_path, '-n', 'get', '-inet6', '2404:6800:400a:800::1012']
  1684. )
  1685.  
  1686. interface = dict(v4 = {}, v6 = {})
  1687.  
  1688. for v in 'v4', 'v6':
  1689.  
  1690. if v == 'v6' and not socket.has_ipv6:
  1691. continue
  1692. rc, out, err = module.run_command(command[v])
  1693. if not out:
  1694. # v6 routing may result in
  1695. # RTNETLINK answers: Invalid argument
  1696. continue
  1697. lines = out.split('\n')
  1698. for line in lines:
  1699. words = line.split()
  1700. # Collect output from route command
  1701. if len(words) > 1:
  1702. if words[0] == 'interface:':
  1703. interface[v]['interface'] = words[1]
  1704. if words[0] == 'gateway:':
  1705. interface[v]['gateway'] = words[1]
  1706.  
  1707. return interface['v4'], interface['v6']
  1708.  
  1709. def get_interfaces_info(self, ifconfig_path):
  1710. interfaces = {}
  1711. current_if = {}
  1712. ips = dict(
  1713. all_ipv4_addresses = [],
  1714. all_ipv6_addresses = [],
  1715. )
  1716. # FreeBSD, DragonflyBSD, NetBSD, OpenBSD and OS X all implicitly add '-a'
  1717. # when running the command 'ifconfig'.
  1718. # Solaris must explicitly run the command 'ifconfig -a'.
  1719. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1720.  
  1721. for line in out.split('\n'):
  1722.  
  1723. if line:
  1724. words = line.split()
  1725.  
  1726. if words[0] == 'pass':
  1727. continue
  1728. elif re.match('^\S', line) and len(words) > 3:
  1729. current_if = self.parse_interface_line(words)
  1730. interfaces[ current_if['device'] ] = current_if
  1731. elif words[0].startswith('options='):
  1732. self.parse_options_line(words, current_if, ips)
  1733. elif words[0] == 'nd6':
  1734. self.parse_nd6_line(words, current_if, ips)
  1735. elif words[0] == 'ether':
  1736. self.parse_ether_line(words, current_if, ips)
  1737. elif words[0] == 'media:':
  1738. self.parse_media_line(words, current_if, ips)
  1739. elif words[0] == 'status:':
  1740. self.parse_status_line(words, current_if, ips)
  1741. elif words[0] == 'lladdr':
  1742. self.parse_lladdr_line(words, current_if, ips)
  1743. elif words[0] == 'inet':
  1744. self.parse_inet_line(words, current_if, ips)
  1745. elif words[0] == 'inet6':
  1746. self.parse_inet6_line(words, current_if, ips)
  1747. else:
  1748. self.parse_unknown_line(words, current_if, ips)
  1749.  
  1750. return interfaces, ips
  1751.  
  1752. def parse_interface_line(self, words):
  1753. device = words[0][0:-1]
  1754. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  1755. current_if['flags'] = self.get_options(words[1])
  1756. current_if['macaddress'] = 'unknown' # will be overwritten later
  1757.  
  1758. if len(words) >= 5 : # Newer FreeBSD versions
  1759. current_if['metric'] = words[3]
  1760. current_if['mtu'] = words[5]
  1761. else:
  1762. current_if['mtu'] = words[3]
  1763.  
  1764. return current_if
  1765.  
  1766. def parse_options_line(self, words, current_if, ips):
  1767. # Mac has options like this...
  1768. current_if['options'] = self.get_options(words[0])
  1769.  
  1770. def parse_nd6_line(self, words, current_if, ips):
  1771. # FreBSD has options like this...
  1772. current_if['options'] = self.get_options(words[1])
  1773.  
  1774. def parse_ether_line(self, words, current_if, ips):
  1775. current_if['macaddress'] = words[1]
  1776.  
  1777. def parse_media_line(self, words, current_if, ips):
  1778. # not sure if this is useful - we also drop information
  1779. current_if['media'] = words[1]
  1780. if len(words) > 2:
  1781. current_if['media_select'] = words[2]
  1782. if len(words) > 3:
  1783. current_if['media_type'] = words[3][1:]
  1784. if len(words) > 4:
  1785. current_if['media_options'] = self.get_options(words[4])
  1786.  
  1787. def parse_status_line(self, words, current_if, ips):
  1788. current_if['status'] = words[1]
  1789.  
  1790. def parse_lladdr_line(self, words, current_if, ips):
  1791. current_if['lladdr'] = words[1]
  1792.  
  1793. def parse_inet_line(self, words, current_if, ips):
  1794. address = {'address': words[1]}
  1795. # deal with hex netmask
  1796. if re.match('([0-9a-f]){8}', words[3]) and len(words[3]) == 8:
  1797. words[3] = '0x' + words[3]
  1798. if words[3].startswith('0x'):
  1799. address['netmask'] = socket.inet_ntoa(struct.pack('!L', int(words[3], base=16)))
  1800. else:
  1801. # otherwise assume this is a dotted quad
  1802. address['netmask'] = words[3]
  1803. # calculate the network
  1804. address_bin = struct.unpack('!L', socket.inet_aton(address['address']))[0]
  1805. netmask_bin = struct.unpack('!L', socket.inet_aton(address['netmask']))[0]
  1806. address['network'] = socket.inet_ntoa(struct.pack('!L', address_bin & netmask_bin))
  1807. # broadcast may be given or we need to calculate
  1808. if len(words) > 5:
  1809. address['broadcast'] = words[5]
  1810. else:
  1811. address['broadcast'] = socket.inet_ntoa(struct.pack('!L', address_bin | (~netmask_bin & 0xffffffff)))
  1812. # add to our list of addresses
  1813. if not words[1].startswith('127.'):
  1814. ips['all_ipv4_addresses'].append(address['address'])
  1815. current_if['ipv4'].append(address)
  1816.  
  1817. def parse_inet6_line(self, words, current_if, ips):
  1818. address = {'address': words[1]}
  1819. if (len(words) >= 4) and (words[2] == 'prefixlen'):
  1820. address['prefix'] = words[3]
  1821. if (len(words) >= 6) and (words[4] == 'scopeid'):
  1822. address['scope'] = words[5]
  1823. localhost6 = ['::1', '::1/128', 'fe80::1%lo0']
  1824. if address['address'] not in localhost6:
  1825. ips['all_ipv6_addresses'].append(address['address'])
  1826. current_if['ipv6'].append(address)
  1827.  
  1828. def parse_unknown_line(self, words, current_if, ips):
  1829. # we are going to ignore unknown lines here - this may be
  1830. # a bad idea - but you can override it in your subclass
  1831. pass
  1832.  
  1833. def get_options(self, option_string):
  1834. start = option_string.find('<') + 1
  1835. end = option_string.rfind('>')
  1836. if (start > 0) and (end > 0) and (end > start + 1):
  1837. option_csv = option_string[start:end]
  1838. return option_csv.split(',')
  1839. else:
  1840. return []
  1841.  
  1842. def merge_default_interface(self, defaults, interfaces, ip_type):
  1843. if not 'interface' in defaults.keys():
  1844. return
  1845. if not defaults['interface'] in interfaces:
  1846. return
  1847. ifinfo = interfaces[defaults['interface']]
  1848. # copy all the interface values across except addresses
  1849. for item in ifinfo.keys():
  1850. if item != 'ipv4' and item != 'ipv6':
  1851. defaults[item] = ifinfo[item]
  1852. if len(ifinfo[ip_type]) > 0:
  1853. for item in ifinfo[ip_type][0].keys():
  1854. defaults[item] = ifinfo[ip_type][0][item]
  1855.  
  1856. class DarwinNetwork(GenericBsdIfconfigNetwork, Network):
  1857. """
  1858. This is the Mac OS X/Darwin Network Class.
  1859. It uses the GenericBsdIfconfigNetwork unchanged
  1860. """
  1861. platform = 'Darwin'
  1862.  
  1863. # media line is different to the default FreeBSD one
  1864. def parse_media_line(self, words, current_if, ips):
  1865. # not sure if this is useful - we also drop information
  1866. current_if['media'] = 'Unknown' # Mac does not give us this
  1867. current_if['media_select'] = words[1]
  1868. if len(words) > 2:
  1869. current_if['media_type'] = words[2][1:]
  1870. if len(words) > 3:
  1871. current_if['media_options'] = self.get_options(words[3])
  1872.  
  1873.  
  1874. class FreeBSDNetwork(GenericBsdIfconfigNetwork, Network):
  1875. """
  1876. This is the FreeBSD Network Class.
  1877. It uses the GenericBsdIfconfigNetwork unchanged.
  1878. """
  1879. platform = 'FreeBSD'
  1880.  
  1881. class AIXNetwork(GenericBsdIfconfigNetwork, Network):
  1882. """
  1883. This is the AIX Network Class.
  1884. It uses the GenericBsdIfconfigNetwork unchanged.
  1885. """
  1886. platform = 'AIX'
  1887.  
  1888. # AIX 'ifconfig -a' does not have three words in the interface line
  1889. def get_interfaces_info(self, ifconfig_path):
  1890. interfaces = {}
  1891. current_if = {}
  1892. ips = dict(
  1893. all_ipv4_addresses = [],
  1894. all_ipv6_addresses = [],
  1895. )
  1896. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1897.  
  1898. for line in out.split('\n'):
  1899.  
  1900. if line:
  1901. words = line.split()
  1902.  
  1903. # only this condition differs from GenericBsdIfconfigNetwork
  1904. if re.match('^\w*\d*:', line):
  1905. current_if = self.parse_interface_line(words)
  1906. interfaces[ current_if['device'] ] = current_if
  1907. elif words[0].startswith('options='):
  1908. self.parse_options_line(words, current_if, ips)
  1909. elif words[0] == 'nd6':
  1910. self.parse_nd6_line(words, current_if, ips)
  1911. elif words[0] == 'ether':
  1912. self.parse_ether_line(words, current_if, ips)
  1913. elif words[0] == 'media:':
  1914. self.parse_media_line(words, current_if, ips)
  1915. elif words[0] == 'status:':
  1916. self.parse_status_line(words, current_if, ips)
  1917. elif words[0] == 'lladdr':
  1918. self.parse_lladdr_line(words, current_if, ips)
  1919. elif words[0] == 'inet':
  1920. self.parse_inet_line(words, current_if, ips)
  1921. elif words[0] == 'inet6':
  1922. self.parse_inet6_line(words, current_if, ips)
  1923. else:
  1924. self.parse_unknown_line(words, current_if, ips)
  1925.  
  1926. return interfaces, ips
  1927.  
  1928. # AIX 'ifconfig -a' does not inform about MTU, so remove current_if['mtu'] here
  1929. def parse_interface_line(self, words):
  1930. device = words[0][0:-1]
  1931. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  1932. current_if['flags'] = self.get_options(words[1])
  1933. current_if['macaddress'] = 'unknown' # will be overwritten later
  1934. return current_if
  1935.  
  1936. class OpenBSDNetwork(GenericBsdIfconfigNetwork, Network):
  1937. """
  1938. This is the OpenBSD Network Class.
  1939. It uses the GenericBsdIfconfigNetwork.
  1940. """
  1941. platform = 'OpenBSD'
  1942.  
  1943. # Return macaddress instead of lladdr
  1944. def parse_lladdr_line(self, words, current_if, ips):
  1945. current_if['macaddress'] = words[1]
  1946.  
  1947. class SunOSNetwork(GenericBsdIfconfigNetwork, Network):
  1948. """
  1949. This is the SunOS Network Class.
  1950. It uses the GenericBsdIfconfigNetwork.
  1951.  
  1952. Solaris can have different FLAGS and MTU for IPv4 and IPv6 on the same interface
  1953. so these facts have been moved inside the 'ipv4' and 'ipv6' lists.
  1954. """
  1955. platform = 'SunOS'
  1956.  
  1957. # Solaris 'ifconfig -a' will print interfaces twice, once for IPv4 and again for IPv6.
  1958. # MTU and FLAGS also may differ between IPv4 and IPv6 on the same interface.
  1959. # 'parse_interface_line()' checks for previously seen interfaces before defining
  1960. # 'current_if' so that IPv6 facts don't clobber IPv4 facts (or vice versa).
  1961. def get_interfaces_info(self, ifconfig_path):
  1962. interfaces = {}
  1963. current_if = {}
  1964. ips = dict(
  1965. all_ipv4_addresses = [],
  1966. all_ipv6_addresses = [],
  1967. )
  1968. rc, out, err = module.run_command([ifconfig_path, '-a'])
  1969.  
  1970. for line in out.split('\n'):
  1971.  
  1972. if line:
  1973. words = line.split()
  1974.  
  1975. if re.match('^\S', line) and len(words) > 3:
  1976. current_if = self.parse_interface_line(words, current_if, interfaces)
  1977. interfaces[ current_if['device'] ] = current_if
  1978. elif words[0].startswith('options='):
  1979. self.parse_options_line(words, current_if, ips)
  1980. elif words[0] == 'nd6':
  1981. self.parse_nd6_line(words, current_if, ips)
  1982. elif words[0] == 'ether':
  1983. self.parse_ether_line(words, current_if, ips)
  1984. elif words[0] == 'media:':
  1985. self.parse_media_line(words, current_if, ips)
  1986. elif words[0] == 'status:':
  1987. self.parse_status_line(words, current_if, ips)
  1988. elif words[0] == 'lladdr':
  1989. self.parse_lladdr_line(words, current_if, ips)
  1990. elif words[0] == 'inet':
  1991. self.parse_inet_line(words, current_if, ips)
  1992. elif words[0] == 'inet6':
  1993. self.parse_inet6_line(words, current_if, ips)
  1994. else:
  1995. self.parse_unknown_line(words, current_if, ips)
  1996.  
  1997. # 'parse_interface_line' and 'parse_inet*_line' leave two dicts in the
  1998. # ipv4/ipv6 lists which is ugly and hard to read.
  1999. # This quick hack merges the dictionaries. Purely cosmetic.
  2000. for iface in interfaces:
  2001. for v in 'ipv4', 'ipv6':
  2002. combined_facts = {}
  2003. for facts in interfaces[iface][v]:
  2004. combined_facts.update(facts)
  2005. if len(combined_facts.keys()) > 0:
  2006. interfaces[iface][v] = [combined_facts]
  2007.  
  2008. return interfaces, ips
  2009.  
  2010. def parse_interface_line(self, words, current_if, interfaces):
  2011. device = words[0][0:-1]
  2012. if device not in interfaces.keys():
  2013. current_if = {'device': device, 'ipv4': [], 'ipv6': [], 'type': 'unknown'}
  2014. else:
  2015. current_if = interfaces[device]
  2016. flags = self.get_options(words[1])
  2017. if 'IPv4' in flags:
  2018. v = 'ipv4'
  2019. if 'IPv6' in flags:
  2020. v = 'ipv6'
  2021. current_if[v].append({'flags': flags, 'mtu': words[3]})
  2022. current_if['macaddress'] = 'unknown' # will be overwritten later
  2023. return current_if
  2024.  
  2025. # Solaris displays single digit octets in MAC addresses e.g. 0:1:2:d:e:f
  2026. # Add leading zero to each octet where needed.
  2027. def parse_ether_line(self, words, current_if, ips):
  2028. macaddress = ''
  2029. for octet in words[1].split(':'):
  2030. octet = ('0' + octet)[-2:None]
  2031. macaddress += (octet + ':')
  2032. current_if['macaddress'] = macaddress[0:-1]
  2033.  
  2034. class Virtual(Facts):
  2035. """
  2036. This is a generic Virtual subclass of Facts. This should be further
  2037. subclassed to implement per platform. If you subclass this,
  2038. you should define:
  2039. - virtualization_type
  2040. - virtualization_role
  2041. - container (e.g. solaris zones, freebsd jails, linux containers)
  2042.  
  2043. All subclasses MUST define platform.
  2044. """
  2045.  
  2046. def __new__(cls, *arguments, **keyword):
  2047. subclass = cls
  2048. for sc in Virtual.__subclasses__():
  2049. if sc.platform == platform.system():
  2050. subclass = sc
  2051. return super(cls, subclass).__new__(subclass, *arguments, **keyword)
  2052.  
  2053. def __init__(self):
  2054. Facts.__init__(self)
  2055.  
  2056. def populate(self):
  2057. return self.facts
  2058.  
  2059. class LinuxVirtual(Virtual):
  2060. """
  2061. This is a Linux-specific subclass of Virtual. It defines
  2062. - virtualization_type
  2063. - virtualization_role
  2064. """
  2065. platform = 'Linux'
  2066.  
  2067. def __init__(self):
  2068. Virtual.__init__(self)
  2069.  
  2070. def populate(self):
  2071. self.get_virtual_facts()
  2072. return self.facts
  2073.  
  2074. # For more information, check: http://people.redhat.com/~rjones/virt-what/
  2075. def get_virtual_facts(self):
  2076. if os.path.exists("/proc/xen"):
  2077. self.facts['virtualization_type'] = 'xen'
  2078. self.facts['virtualization_role'] = 'guest'
  2079. try:
  2080. for line in open('/proc/xen/capabilities'):
  2081. if "control_d" in line:
  2082. self.facts['virtualization_role'] = 'host'
  2083. except IOError:
  2084. pass
  2085. return
  2086.  
  2087. if os.path.exists('/proc/vz'):
  2088. self.facts['virtualization_type'] = 'openvz'
  2089. if os.path.exists('/proc/bc'):
  2090. self.facts['virtualization_role'] = 'host'
  2091. else:
  2092. self.facts['virtualization_role'] = 'guest'
  2093. return
  2094.  
  2095. if os.path.exists('/proc/1/cgroup'):
  2096. for line in open('/proc/1/cgroup').readlines():
  2097. if re.search('/lxc/', line):
  2098. self.facts['virtualization_type'] = 'lxc'
  2099. self.facts['virtualization_role'] = 'guest'
  2100. return
  2101.  
  2102. product_name = get_file_content('/sys/devices/virtual/dmi/id/product_name')
  2103.  
  2104. if product_name in ['KVM', 'Bochs']:
  2105. self.facts['virtualization_type'] = 'kvm'
  2106. self.facts['virtualization_role'] = 'guest'
  2107. return
  2108.  
  2109. if product_name == 'RHEV Hypervisor':
  2110. self.facts['virtualization_type'] = 'RHEV'
  2111. self.facts['virtualization_role'] = 'guest'
  2112. return
  2113.  
  2114. if product_name == 'VMware Virtual Platform':
  2115. self.facts['virtualization_type'] = 'VMware'
  2116. self.facts['virtualization_role'] = 'guest'
  2117. return
  2118.  
  2119. bios_vendor = get_file_content('/sys/devices/virtual/dmi/id/bios_vendor')
  2120.  
  2121. if bios_vendor == 'Xen':
  2122. self.facts['virtualization_type'] = 'xen'
  2123. self.facts['virtualization_role'] = 'guest'
  2124. return
  2125.  
  2126. if bios_vendor == 'innotek GmbH':
  2127. self.facts['virtualization_type'] = 'virtualbox'
  2128. self.facts['virtualization_role'] = 'guest'
  2129. return
  2130.  
  2131. sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor')
  2132.  
  2133. # FIXME: This does also match hyperv
  2134. if sys_vendor == 'Microsoft Corporation':
  2135. self.facts['virtualization_type'] = 'VirtualPC'
  2136. self.facts['virtualization_role'] = 'guest'
  2137. return
  2138.  
  2139. if sys_vendor == 'Parallels Software International Inc.':
  2140. self.facts['virtualization_type'] = 'parallels'
  2141. self.facts['virtualization_role'] = 'guest'
  2142. return
  2143.  
  2144. if os.path.exists('/proc/self/status'):
  2145. for line in open('/proc/self/status').readlines():
  2146. if re.match('^VxID: \d+', line):
  2147. self.facts['virtualization_type'] = 'linux_vserver'
  2148. if re.match('^VxID: 0', line):
  2149. self.facts['virtualization_role'] = 'host'
  2150. else:
  2151. self.facts['virtualization_role'] = 'guest'
  2152. return
  2153.  
  2154. if os.path.exists('/proc/cpuinfo'):
  2155. for line in open('/proc/cpuinfo').readlines():
  2156. if re.match('^model name.*QEMU Virtual CPU', line):
  2157. self.facts['virtualization_type'] = 'kvm'
  2158. elif re.match('^vendor_id.*User Mode Linux', line):
  2159. self.facts['virtualization_type'] = 'uml'
  2160. elif re.match('^model name.*UML', line):
  2161. self.facts['virtualization_type'] = 'uml'
  2162. elif re.match('^vendor_id.*PowerVM Lx86', line):
  2163. self.facts['virtualization_type'] = 'powervm_lx86'
  2164. elif re.match('^vendor_id.*IBM/S390', line):
  2165. self.facts['virtualization_type'] = 'ibm_systemz'
  2166. else:
  2167. continue
  2168. self.facts['virtualization_role'] = 'guest'
  2169. return
  2170.  
  2171. # Beware that we can have both kvm and virtualbox running on a single system
  2172. if os.path.exists("/proc/modules") and os.access('/proc/modules', os.R_OK):
  2173. modules = []
  2174. for line in open("/proc/modules").readlines():
  2175. data = line.split(" ", 1)
  2176. modules.append(data[0])
  2177.  
  2178. if 'kvm' in modules:
  2179. self.facts['virtualization_type'] = 'kvm'
  2180. self.facts['virtualization_role'] = 'host'
  2181. return
  2182.  
  2183. if 'vboxdrv' in modules:
  2184. self.facts['virtualization_type'] = 'virtualbox'
  2185. self.facts['virtualization_role'] = 'host'
  2186. return
  2187.  
  2188. # If none of the above matches, return 'NA' for virtualization_type
  2189. # and virtualization_role. This allows for proper grouping.
  2190. self.facts['virtualization_type'] = 'NA'
  2191. self.facts['virtualization_role'] = 'NA'
  2192. return
  2193.  
  2194.  
  2195. class HPUXVirtual(Virtual):
  2196. """
  2197. This is a HP-UX specific subclass of Virtual. It defines
  2198. - virtualization_type
  2199. - virtualization_role
  2200. """
  2201. platform = 'HP-UX'
  2202.  
  2203. def __init__(self):
  2204. Virtual.__init__(self)
  2205.  
  2206. def populate(self):
  2207. self.get_virtual_facts()
  2208. return self.facts
  2209.  
  2210. def get_virtual_facts(self):
  2211. if os.path.exists('/usr/sbin/vecheck'):
  2212. rc, out, err = module.run_command("/usr/sbin/vecheck")
  2213. if rc == 0:
  2214. self.facts['virtualization_type'] = 'guest'
  2215. self.facts['virtualization_role'] = 'HP vPar'
  2216. if os.path.exists('/opt/hpvm/bin/hpvminfo'):
  2217. rc, out, err = module.run_command("/opt/hpvm/bin/hpvminfo")
  2218. if rc == 0 and re.match('.*Running.*HPVM vPar.*', out):
  2219. self.facts['virtualization_type'] = 'guest'
  2220. self.facts['virtualization_role'] = 'HPVM vPar'
  2221. elif rc == 0 and re.match('.*Running.*HPVM guest.*', out):
  2222. self.facts['virtualization_type'] = 'guest'
  2223. self.facts['virtualization_role'] = 'HPVM IVM'
  2224. elif rc == 0 and re.match('.*Running.*HPVM host.*', out):
  2225. self.facts['virtualization_type'] = 'host'
  2226. self.facts['virtualization_role'] = 'HPVM'
  2227. if os.path.exists('/usr/sbin/parstatus'):
  2228. rc, out, err = module.run_command("/usr/sbin/parstatus")
  2229. if rc == 0:
  2230. self.facts['virtualization_type'] = 'guest'
  2231. self.facts['virtualization_role'] = 'HP nPar'
  2232.  
  2233.  
  2234. class SunOSVirtual(Virtual):
  2235. """
  2236. This is a SunOS-specific subclass of Virtual. It defines
  2237. - virtualization_type
  2238. - virtualization_role
  2239. - container
  2240. """
  2241. platform = 'SunOS'
  2242.  
  2243. def __init__(self):
  2244. Virtual.__init__(self)
  2245.  
  2246. def populate(self):
  2247. self.get_virtual_facts()
  2248. return self.facts
  2249.  
  2250. def get_virtual_facts(self):
  2251. rc, out, err = module.run_command("/usr/sbin/prtdiag")
  2252. for line in out.split('\n'):
  2253. if 'VMware' in line:
  2254. self.facts['virtualization_type'] = 'vmware'
  2255. self.facts['virtualization_role'] = 'guest'
  2256. if 'Parallels' in line:
  2257. self.facts['virtualization_type'] = 'parallels'
  2258. self.facts['virtualization_role'] = 'guest'
  2259. if 'VirtualBox' in line:
  2260. self.facts['virtualization_type'] = 'virtualbox'
  2261. self.facts['virtualization_role'] = 'guest'
  2262. if 'HVM domU' in line:
  2263. self.facts['virtualization_type'] = 'xen'
  2264. self.facts['virtualization_role'] = 'guest'
  2265. # Check if it's a zone
  2266. if os.path.exists("/usr/bin/zonename"):
  2267. rc, out, err = module.run_command("/usr/bin/zonename")
  2268. if out.rstrip() != "global":
  2269. self.facts['container'] = 'zone'
  2270. # Check if it's a branded zone (i.e. Solaris 8/9 zone)
  2271. if os.path.isdir('/.SUNWnative'):
  2272. self.facts['container'] = 'zone'
  2273. # If it's a zone check if we can detect if our global zone is itself virtualized.
  2274. # Relies on the "guest tools" (e.g. vmware tools) to be installed
  2275. if 'container' in self.facts and self.facts['container'] == 'zone':
  2276. rc, out, err = module.run_command("/usr/sbin/modinfo")
  2277. for line in out.split('\n'):
  2278. if 'VMware' in line:
  2279. self.facts['virtualization_type'] = 'vmware'
  2280. self.facts['virtualization_role'] = 'guest'
  2281. if 'VirtualBox' in line:
  2282. self.facts['virtualization_type'] = 'virtualbox'
  2283. self.facts['virtualization_role'] = 'guest'
  2284.  
  2285. def get_file_content(path, default=None):
  2286. data = default
  2287. if os.path.exists(path) and os.access(path, os.R_OK):
  2288. data = open(path).read().strip()
  2289. if len(data) == 0:
  2290. data = default
  2291. return data
  2292.  
  2293. def ansible_facts(module):
  2294. facts = {}
  2295. facts.update(Facts().populate())
  2296. facts.update(Hardware().populate())
  2297. facts.update(Network(module).populate())
  2298. facts.update(Virtual().populate())
  2299. return facts
  2300.  
  2301. # ===========================================
  2302.  
  2303. def get_all_facts(module):
  2304.  
  2305. setup_options = dict(module_setup=True)
  2306. facts = ansible_facts(module)
  2307.  
  2308. for (k, v) in facts.items():
  2309. setup_options["ansible_%s" % k.replace('-', '_')] = v
  2310.  
  2311. # Look for the path to the facter and ohai binary and set
  2312. # the variable to that path.
  2313.  
  2314. facter_path = module.get_bin_path('facter')
  2315. ohai_path = module.get_bin_path('ohai')
  2316.  
  2317. # if facter is installed, and we can use --json because
  2318. # ruby-json is ALSO installed, include facter data in the JSON
  2319.  
  2320. if facter_path is not None:
  2321. rc, out, err = module.run_command(facter_path + " --json")
  2322. facter = True
  2323. try:
  2324. facter_ds = json.loads(out)
  2325. except:
  2326. facter = False
  2327. if facter:
  2328. for (k,v) in facter_ds.items():
  2329. setup_options["facter_%s" % k] = v
  2330.  
  2331. # ditto for ohai
  2332.  
  2333. if ohai_path is not None:
  2334. rc, out, err = module.run_command(ohai_path)
  2335. ohai = True
  2336. try:
  2337. ohai_ds = json.loads(out)
  2338. except:
  2339. ohai = False
  2340. if ohai:
  2341. for (k,v) in ohai_ds.items():
  2342. k2 = "ohai_%s" % k.replace('-', '_')
  2343. setup_options[k2] = v
  2344.  
  2345. setup_result = { 'ansible_facts': {} }
  2346.  
  2347. for (k,v) in setup_options.items():
  2348. if module.params['filter'] == '*' or fnmatch.fnmatch(k, module.params['filter']):
  2349. setup_result['ansible_facts'][k] = v
  2350.  
  2351. # hack to keep --verbose from showing all the setup module results
  2352. setup_result['verbose_override'] = True
  2353.  
  2354. return setup_result
Advertisement
Add Comment
Please, Sign In to add comment