/lib/ansible/module_utils/facts.py
https://github.com/ajanthanm/ansible · Python · 2371 lines · 1776 code · 264 blank · 331 comment · 531 complexity · a9c77d38aded62bddd620d444873d9fa MD5 · raw file
Large files are truncated click here to view the full file
- # (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
- #
- # This file is part of Ansible
- #
- # Ansible is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Ansible is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- import os
- import array
- import errno
- import fcntl
- import fnmatch
- import glob
- import platform
- import re
- import signal
- import socket
- import struct
- import datetime
- import getpass
- import ConfigParser
- import StringIO
- try:
- import selinux
- HAVE_SELINUX=True
- except ImportError:
- HAVE_SELINUX=False
- try:
- import json
- except ImportError:
- import simplejson as json
- # --------------------------------------------------------------
- # timeout function to make sure some fact gathering
- # steps do not exceed a time limit
- class TimeoutError(Exception):
- pass
- def timeout(seconds=10, error_message="Timer expired"):
- def decorator(func):
- def _handle_timeout(signum, frame):
- raise TimeoutError(error_message)
- def wrapper(*args, **kwargs):
- signal.signal(signal.SIGALRM, _handle_timeout)
- signal.alarm(seconds)
- try:
- result = func(*args, **kwargs)
- finally:
- signal.alarm(0)
- return result
- return wrapper
- return decorator
- # --------------------------------------------------------------
- class Facts(object):
- """
- This class should only attempt to populate those facts that
- are mostly generic to all systems. This includes platform facts,
- service facts (eg. ssh keys or selinux), and distribution facts.
- Anything that requires extensive code or may have more than one
- possible implementation to establish facts for a given topic should
- subclass Facts.
- """
- _I386RE = re.compile(r'i[3456]86')
- # For the most part, we assume that platform.dist() will tell the truth.
- # This is the fallback to handle unknowns or exceptions
- OSDIST_DICT = { '/etc/redhat-release': 'RedHat',
- '/etc/vmware-release': 'VMwareESX',
- '/etc/openwrt_release': 'OpenWrt',
- '/etc/system-release': 'OtherLinux',
- '/etc/alpine-release': 'Alpine',
- '/etc/release': 'Solaris',
- '/etc/arch-release': 'Archlinux',
- '/etc/SuSE-release': 'SuSE',
- '/etc/gentoo-release': 'Gentoo',
- '/etc/os-release': 'Debian' }
- SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' }
- # A list of dicts. If there is a platform with more than one
- # package manager, put the preferred one last. If there is an
- # ansible module, use that as the value for the 'name' key.
- PKG_MGRS = [ { 'path' : '/usr/bin/yum', 'name' : 'yum' },
- { 'path' : '/usr/bin/apt-get', 'name' : 'apt' },
- { 'path' : '/usr/bin/zypper', 'name' : 'zypper' },
- { 'path' : '/usr/sbin/urpmi', 'name' : 'urpmi' },
- { 'path' : '/usr/bin/pacman', 'name' : 'pacman' },
- { 'path' : '/bin/opkg', 'name' : 'opkg' },
- { 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' },
- { 'path' : '/opt/local/bin/port', 'name' : 'macports' },
- { 'path' : '/sbin/apk', 'name' : 'apk' },
- { 'path' : '/usr/sbin/pkg', 'name' : 'pkgng' },
- { 'path' : '/usr/sbin/swlist', 'name' : 'SD-UX' },
- { 'path' : '/usr/bin/emerge', 'name' : 'portage' },
- { 'path' : '/usr/sbin/pkgadd', 'name' : 'svr4pkg' },
- { 'path' : '/usr/bin/pkg', 'name' : 'pkg' },
- ]
- def __init__(self):
- self.facts = {}
- self.get_platform_facts()
- self.get_distribution_facts()
- self.get_cmdline()
- self.get_public_ssh_host_keys()
- self.get_selinux_facts()
- self.get_pkg_mgr_facts()
- self.get_lsb_facts()
- self.get_date_time_facts()
- self.get_user_facts()
- self.get_local_facts()
- self.get_env_facts()
- def populate(self):
- return self.facts
- # Platform
- # platform.system() can be Linux, Darwin, Java, or Windows
- def get_platform_facts(self):
- self.facts['system'] = platform.system()
- self.facts['kernel'] = platform.release()
- self.facts['machine'] = platform.machine()
- self.facts['python_version'] = platform.python_version()
- self.facts['fqdn'] = socket.getfqdn()
- self.facts['hostname'] = platform.node().split('.')[0]
- self.facts['nodename'] = platform.node()
- self.facts['domain'] = '.'.join(self.facts['fqdn'].split('.')[1:])
- arch_bits = platform.architecture()[0]
- self.facts['userspace_bits'] = arch_bits.replace('bit', '')
- if self.facts['machine'] == 'x86_64':
- self.facts['architecture'] = self.facts['machine']
- if self.facts['userspace_bits'] == '64':
- self.facts['userspace_architecture'] = 'x86_64'
- elif self.facts['userspace_bits'] == '32':
- self.facts['userspace_architecture'] = 'i386'
- elif Facts._I386RE.search(self.facts['machine']):
- self.facts['architecture'] = 'i386'
- if self.facts['userspace_bits'] == '64':
- self.facts['userspace_architecture'] = 'x86_64'
- elif self.facts['userspace_bits'] == '32':
- self.facts['userspace_architecture'] = 'i386'
- else:
- self.facts['architecture'] = self.facts['machine']
- if self.facts['system'] == 'Linux':
- self.get_distribution_facts()
- elif self.facts['system'] == 'AIX':
- rc, out, err = module.run_command("/usr/sbin/bootinfo -p")
- data = out.split('\n')
- self.facts['architecture'] = data[0]
- def get_local_facts(self):
- fact_path = module.params.get('fact_path', None)
- if not fact_path or not os.path.exists(fact_path):
- return
- local = {}
- for fn in sorted(glob.glob(fact_path + '/*.fact')):
- # where it will sit under local facts
- fact_base = os.path.basename(fn).replace('.fact','')
- if os.access(fn, os.X_OK):
- # run it
- # try to read it as json first
- # if that fails read it with ConfigParser
- # if that fails, skip it
- rc, out, err = module.run_command(fn)
- else:
- out = open(fn).read()
- # load raw json
- fact = 'loading %s' % fact_base
- try:
- fact = json.loads(out)
- except ValueError, e:
- # load raw ini
- cp = ConfigParser.ConfigParser()
- try:
- cp.readfp(StringIO.StringIO(out))
- except ConfigParser.Error, e:
- fact="error loading fact - please check content"
- else:
- fact = {}
- #print cp.sections()
- for sect in cp.sections():
- if sect not in fact:
- fact[sect] = {}
- for opt in cp.options(sect):
- val = cp.get(sect, opt)
- fact[sect][opt]=val
- local[fact_base] = fact
- if not local:
- return
- self.facts['local'] = local
- # platform.dist() is deprecated in 2.6
- # in 2.6 and newer, you should use platform.linux_distribution()
- def get_distribution_facts(self):
- # A list with OS Family members
- OS_FAMILY = dict(
- RedHat = 'RedHat', Fedora = 'RedHat', CentOS = 'RedHat', Scientific = 'RedHat',
- SLC = 'RedHat', Ascendos = 'RedHat', CloudLinux = 'RedHat', PSBM = 'RedHat',
- OracleLinux = 'RedHat', OVS = 'RedHat', OEL = 'RedHat', Amazon = 'RedHat',
- XenServer = 'RedHat', Ubuntu = 'Debian', Debian = 'Debian', SLES = 'Suse',
- SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', Funtoo = 'Gentoo',
- Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
- Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris',
- SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin',
- FreeBSD = 'FreeBSD', HPUX = 'HP-UX'
- )
- if self.facts['system'] == 'AIX':
- self.facts['distribution'] = 'AIX'
- rc, out, err = module.run_command("/usr/bin/oslevel")
- data = out.split('.')
- self.facts['distribution_version'] = data[0]
- self.facts['distribution_release'] = data[1]
- elif self.facts['system'] == 'HP-UX':
- self.facts['distribution'] = 'HP-UX'
- rc, out, err = module.run_command("/usr/sbin/swlist |egrep 'HPUX.*OE.*[AB].[0-9]+\.[0-9]+'", use_unsafe_shell=True)
- data = re.search('HPUX.*OE.*([AB].[0-9]+\.[0-9]+)\.([0-9]+).*', out)
- if data:
- self.facts['distribution_version'] = data.groups()[0]
- self.facts['distribution_release'] = data.groups()[1]
- elif self.facts['system'] == 'Darwin':
- self.facts['distribution'] = 'MacOSX'
- rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion")
- data = out.split()[-1]
- self.facts['distribution_version'] = data
- elif self.facts['system'] == 'FreeBSD':
- self.facts['distribution'] = 'FreeBSD'
- self.facts['distribution_release'] = platform.release()
- self.facts['distribution_version'] = platform.version()
- elif self.facts['system'] == 'OpenBSD':
- self.facts['distribution'] = 'OpenBSD'
- self.facts['distribution_release'] = platform.release()
- rc, out, err = module.run_command("/sbin/sysctl -n kern.version")
- match = re.match('OpenBSD\s[0-9]+.[0-9]+-(\S+)\s.*', out)
- if match:
- self.facts['distribution_version'] = match.groups()[0]
- else:
- self.facts['distribution_version'] = 'release'
- else:
- dist = platform.dist()
- self.facts['distribution'] = dist[0].capitalize() or 'NA'
- self.facts['distribution_version'] = dist[1] or 'NA'
- self.facts['distribution_major_version'] = dist[1].split('.')[0] or 'NA'
- self.facts['distribution_release'] = dist[2] or 'NA'
- # Try to handle the exceptions now ...
- for (path, name) in Facts.OSDIST_DICT.items():
- if os.path.exists(path) and os.path.getsize(path) > 0:
- if self.facts['distribution'] == 'Fedora':
- pass
- elif name == 'RedHat':
- data = get_file_content(path)
- if 'Red Hat' in data:
- self.facts['distribution'] = name
- else:
- self.facts['distribution'] = data.split()[0]
- elif name == 'OtherLinux':
- data = get_file_content(path)
- if 'Amazon' in data:
- self.facts['distribution'] = 'Amazon'
- self.facts['distribution_version'] = data.split()[-1]
- elif name == 'OpenWrt':
- data = get_file_content(path)
- if 'OpenWrt' in data:
- self.facts['distribution'] = name
- version = re.search('DISTRIB_RELEASE="(.*)"', data)
- if version:
- self.facts['distribution_version'] = version.groups()[0]
- release = re.search('DISTRIB_CODENAME="(.*)"', data)
- if release:
- self.facts['distribution_release'] = release.groups()[0]
- elif name == 'Alpine':
- data = get_file_content(path)
- self.facts['distribution'] = 'Alpine'
- self.facts['distribution_version'] = data
- elif name == 'Solaris':
- data = get_file_content(path).split('\n')[0]
- ora_prefix = ''
- if 'Oracle Solaris' in data:
- data = data.replace('Oracle ','')
- ora_prefix = 'Oracle '
- self.facts['distribution'] = data.split()[0]
- self.facts['distribution_version'] = data.split()[1]
- self.facts['distribution_release'] = ora_prefix + data
- elif name == 'SuSE':
- data = get_file_content(path).splitlines()
- for line in data:
- if '=' in line:
- self.facts['distribution_release'] = line.split('=')[1].strip()
- elif name == 'Debian':
- data = get_file_content(path).split('\n')[0]
- release = re.search("PRETTY_NAME.+ \(?([^ ]+?)\)?\"", data)
- if release:
- self.facts['distribution_release'] = release.groups()[0]
- else:
- self.facts['distribution'] = name
- self.facts['os_family'] = self.facts['distribution']
- if self.facts['distribution'] in OS_FAMILY:
- self.facts['os_family'] = OS_FAMILY[self.facts['distribution']]
- def get_cmdline(self):
- data = get_file_content('/proc/cmdline')
- if data:
- self.facts['cmdline'] = {}
- for piece in shlex.split(data):
- item = piece.split('=', 1)
- if len(item) == 1:
- self.facts['cmdline'][item[0]] = True
- else:
- self.facts['cmdline'][item[0]] = item[1]
- def get_public_ssh_host_keys(self):
- dsa_filename = '/etc/ssh/ssh_host_dsa_key.pub'
- rsa_filename = '/etc/ssh/ssh_host_rsa_key.pub'
- ecdsa_filename = '/etc/ssh/ssh_host_ecdsa_key.pub'
- if self.facts['system'] == 'Darwin':
- dsa_filename = '/etc/ssh_host_dsa_key.pub'
- rsa_filename = '/etc/ssh_host_rsa_key.pub'
- ecdsa_filename = '/etc/ssh_host_ecdsa_key.pub'
- dsa = get_file_content(dsa_filename)
- rsa = get_file_content(rsa_filename)
- ecdsa = get_file_content(ecdsa_filename)
- if dsa is None:
- dsa = 'NA'
- else:
- self.facts['ssh_host_key_dsa_public'] = dsa.split()[1]
- if rsa is None:
- rsa = 'NA'
- else:
- self.facts['ssh_host_key_rsa_public'] = rsa.split()[1]
- if ecdsa is None:
- ecdsa = 'NA'
- else:
- self.facts['ssh_host_key_ecdsa_public'] = ecdsa.split()[1]
- def get_pkg_mgr_facts(self):
- self.facts['pkg_mgr'] = 'unknown'
- for pkg in Facts.PKG_MGRS:
- if os.path.exists(pkg['path']):
- self.facts['pkg_mgr'] = pkg['name']
- if self.facts['system'] == 'OpenBSD':
- self.facts['pkg_mgr'] = 'openbsd_pkg'
- def get_lsb_facts(self):
- lsb_path = module.get_bin_path('lsb_release')
- if lsb_path:
- rc, out, err = module.run_command([lsb_path, "-a"])
- if rc == 0:
- self.facts['lsb'] = {}
- for line in out.split('\n'):
- if len(line) < 1:
- continue
- value = line.split(':', 1)[1].strip()
- if 'LSB Version:' in line:
- self.facts['lsb']['release'] = value
- elif 'Distributor ID:' in line:
- self.facts['lsb']['id'] = value
- elif 'Description:' in line:
- self.facts['lsb']['description'] = value
- elif 'Release:' in line:
- self.facts['lsb']['release'] = value
- elif 'Codename:' in line:
- self.facts['lsb']['codename'] = value
- if 'lsb' in self.facts and 'release' in self.facts['lsb']:
- self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
- elif lsb_path is None and os.path.exists('/etc/lsb-release'):
- self.facts['lsb'] = {}
- f = open('/etc/lsb-release', 'r')
- try:
- for line in f.readlines():
- value = line.split('=',1)[1].strip()
- if 'DISTRIB_ID' in line:
- self.facts['lsb']['id'] = value
- elif 'DISTRIB_RELEASE' in line:
- self.facts['lsb']['release'] = value
- elif 'DISTRIB_DESCRIPTION' in line:
- self.facts['lsb']['description'] = value
- elif 'DISTRIB_CODENAME' in line:
- self.facts['lsb']['codename'] = value
- finally:
- f.close()
- else:
- return self.facts
- if 'lsb' in self.facts and 'release' in self.facts['lsb']:
- self.facts['lsb']['major_release'] = self.facts['lsb']['release'].split('.')[0]
- def get_selinux_facts(self):
- if not HAVE_SELINUX:
- self.facts['selinux'] = False
- return
- self.facts['selinux'] = {}
- if not selinux.is_selinux_enabled():
- self.facts['selinux']['status'] = 'disabled'
- else:
- self.facts['selinux']['status'] = 'enabled'
- try:
- self.facts['selinux']['policyvers'] = selinux.security_policyvers()
- except OSError, e:
- self.facts['selinux']['policyvers'] = 'unknown'
- try:
- (rc, configmode) = selinux.selinux_getenforcemode()
- if rc == 0:
- self.facts['selinux']['config_mode'] = Facts.SELINUX_MODE_DICT.get(configmode, 'unknown')
- else:
- self.facts['selinux']['config_mode'] = 'unknown'
- except OSError, e:
- self.facts['selinux']['config_mode'] = 'unknown'
- try:
- mode = selinux.security_getenforce()
- self.facts['selinux']['mode'] = Facts.SELINUX_MODE_DICT.get(mode, 'unknown')
- except OSError, e:
- self.facts['selinux']['mode'] = 'unknown'
- try:
- (rc, policytype) = selinux.selinux_getpolicytype()
- if rc == 0:
- self.facts['selinux']['type'] = policytype
- else:
- self.facts['selinux']['type'] = 'unknown'
- except OSError, e:
- self.facts['selinux']['type'] = 'unknown'
- def get_date_time_facts(self):
- self.facts['date_time'] = {}
- now = datetime.datetime.now()
- self.facts['date_time']['year'] = now.strftime('%Y')
- self.facts['date_time']['month'] = now.strftime('%m')
- self.facts['date_time']['weekday'] = now.strftime('%A')
- self.facts['date_time']['day'] = now.strftime('%d')
- self.facts['date_time']['hour'] = now.strftime('%H')
- self.facts['date_time']['minute'] = now.strftime('%M')
- self.facts['date_time']['second'] = now.strftime('%S')
- self.facts['date_time']['epoch'] = now.strftime('%s')
- if self.facts['date_time']['epoch'] == '' or self.facts['date_time']['epoch'][0] == '%':
- self.facts['date_time']['epoch'] = str(int(time.time()))
- self.facts['date_time']['date'] = now.strftime('%Y-%m-%d')
- self.facts['date_time']['time'] = now.strftime('%H:%M:%S')
- self.facts['date_time']['iso8601_micro'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
- self.facts['date_time']['iso8601'] = now.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
- self.facts['date_time']['tz'] = time.strftime("%Z")
- self.facts['date_time']['tz_offset'] = time.strftime("%z")
- # User
- def get_user_facts(self):
- self.facts['user_id'] = getpass.getuser()
- def get_env_facts(self):
- self.facts['env'] = {}
- for k,v in os.environ.iteritems():
- self.facts['env'][k] = v
- class Hardware(Facts):
- """
- This is a generic Hardware subclass of Facts. This should be further
- subclassed to implement per platform. If you subclass this, it
- should define:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- All subclasses MUST define platform.
- """
- platform = 'Generic'
- def __new__(cls, *arguments, **keyword):
- subclass = cls
- for sc in Hardware.__subclasses__():
- if sc.platform == platform.system():
- subclass = sc
- return super(cls, subclass).__new__(subclass, *arguments, **keyword)
- def __init__(self):
- Facts.__init__(self)
- def populate(self):
- return self.facts
- class LinuxHardware(Hardware):
- """
- Linux-specific subclass of Hardware. Defines memory and CPU facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- In addition, it also defines number of DMI facts and device facts.
- """
- platform = 'Linux'
- MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- self.get_dmi_facts()
- self.get_device_facts()
- try:
- self.get_mount_facts()
- except TimeoutError:
- pass
- return self.facts
- def get_memory_facts(self):
- if not os.access("/proc/meminfo", os.R_OK):
- return
- for line in open("/proc/meminfo").readlines():
- data = line.split(":", 1)
- key = data[0]
- if key in LinuxHardware.MEMORY_FACTS:
- val = data[1].strip().split(' ')[0]
- self.facts["%s_mb" % key.lower()] = long(val) / 1024
- def get_cpu_facts(self):
- i = 0
- physid = 0
- coreid = 0
- sockets = {}
- cores = {}
- if not os.access("/proc/cpuinfo", os.R_OK):
- return
- self.facts['processor'] = []
- for line in open("/proc/cpuinfo").readlines():
- data = line.split(":", 1)
- key = data[0].strip()
- # model name is for Intel arch, Processor (mind the uppercase P)
- # works for some ARM devices, like the Sheevaplug.
- if key == 'model name' or key == 'Processor':
- if 'processor' not in self.facts:
- self.facts['processor'] = []
- self.facts['processor'].append(data[1].strip())
- i += 1
- elif key == 'physical id':
- physid = data[1].strip()
- if physid not in sockets:
- sockets[physid] = 1
- elif key == 'core id':
- coreid = data[1].strip()
- if coreid not in sockets:
- cores[coreid] = 1
- elif key == 'cpu cores':
- sockets[physid] = int(data[1].strip())
- elif key == 'siblings':
- cores[coreid] = int(data[1].strip())
- self.facts['processor_count'] = sockets and len(sockets) or i
- self.facts['processor_cores'] = sockets.values() and sockets.values()[0] or 1
- self.facts['processor_threads_per_core'] = ((cores.values() and
- cores.values()[0] or 1) / self.facts['processor_cores'])
- self.facts['processor_vcpus'] = (self.facts['processor_threads_per_core'] *
- self.facts['processor_count'] * self.facts['processor_cores'])
- def get_dmi_facts(self):
- ''' learn dmi facts from system
- Try /sys first for dmi related facts.
- If that is not available, fall back to dmidecode executable '''
- if os.path.exists('/sys/devices/virtual/dmi/id/product_name'):
- # Use kernel DMI info, if available
- # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
- FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop",
- "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
- "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station",
- "All In One", "Sub Notebook", "Space-saving", "Lunch Box",
- "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
- "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis",
- "Rack Mount Chassis", "Sealed-case PC", "Multi-system",
- "CompactPCI", "AdvancedTCA", "Blade" ]
- DMI_DICT = {
- 'bios_date': '/sys/devices/virtual/dmi/id/bios_date',
- 'bios_version': '/sys/devices/virtual/dmi/id/bios_version',
- 'form_factor': '/sys/devices/virtual/dmi/id/chassis_type',
- 'product_name': '/sys/devices/virtual/dmi/id/product_name',
- 'product_serial': '/sys/devices/virtual/dmi/id/product_serial',
- 'product_uuid': '/sys/devices/virtual/dmi/id/product_uuid',
- 'product_version': '/sys/devices/virtual/dmi/id/product_version',
- 'system_vendor': '/sys/devices/virtual/dmi/id/sys_vendor'
- }
- for (key,path) in DMI_DICT.items():
- data = get_file_content(path)
- if data is not None:
- if key == 'form_factor':
- try:
- self.facts['form_factor'] = FORM_FACTOR[int(data)]
- except IndexError, e:
- self.facts['form_factor'] = 'unknown (%s)' % data
- else:
- self.facts[key] = data
- else:
- self.facts[key] = 'NA'
- else:
- # Fall back to using dmidecode, if available
- dmi_bin = module.get_bin_path('dmidecode')
- DMI_DICT = {
- 'bios_date': 'bios-release-date',
- 'bios_version': 'bios-version',
- 'form_factor': 'chassis-type',
- 'product_name': 'system-product-name',
- 'product_serial': 'system-serial-number',
- 'product_uuid': 'system-uuid',
- 'product_version': 'system-version',
- 'system_vendor': 'system-manufacturer'
- }
- for (k, v) in DMI_DICT.items():
- if dmi_bin is not None:
- (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
- if rc == 0:
- # Strip out commented lines (specific dmidecode output)
- thisvalue = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
- try:
- json.dumps(thisvalue)
- except UnicodeDecodeError:
- thisvalue = "NA"
- self.facts[k] = thisvalue
- else:
- self.facts[k] = 'NA'
- else:
- self.facts[k] = 'NA'
- @timeout(10)
- def get_mount_facts(self):
- self.facts['mounts'] = []
- mtab = get_file_content('/etc/mtab', '')
- for line in mtab.split('\n'):
- if line.startswith('/'):
- fields = line.rstrip('\n').split()
- if(fields[2] != 'none'):
- size_total = None
- size_available = None
- try:
- statvfs_result = os.statvfs(fields[1])
- size_total = statvfs_result.f_bsize * statvfs_result.f_blocks
- size_available = statvfs_result.f_bsize * (statvfs_result.f_bavail)
- except OSError, e:
- continue
- self.facts['mounts'].append(
- {'mount': fields[1],
- 'device':fields[0],
- 'fstype': fields[2],
- 'options': fields[3],
- # statvfs data
- 'size_total': size_total,
- 'size_available': size_available,
- })
- def get_device_facts(self):
- self.facts['devices'] = {}
- lspci = module.get_bin_path('lspci')
- if lspci:
- rc, pcidata, err = module.run_command([lspci, '-D'])
- else:
- pcidata = None
- try:
- block_devs = os.listdir("/sys/block")
- except OSError:
- return
- for block in block_devs:
- virtual = 1
- sysfs_no_links = 0
- try:
- path = os.readlink(os.path.join("/sys/block/", block))
- except OSError, e:
- if e.errno == errno.EINVAL:
- path = block
- sysfs_no_links = 1
- else:
- continue
- if "virtual" in path:
- continue
- sysdir = os.path.join("/sys/block", path)
- if sysfs_no_links == 1:
- for folder in os.listdir(sysdir):
- if "device" in folder:
- virtual = 0
- break
- if virtual:
- continue
- d = {}
- diskname = os.path.basename(sysdir)
- for key in ['vendor', 'model']:
- d[key] = get_file_content(sysdir + "/device/" + key)
- for key,test in [ ('removable','/removable'), \
- ('support_discard','/queue/discard_granularity'),
- ]:
- d[key] = get_file_content(sysdir + test)
- d['partitions'] = {}
- for folder in os.listdir(sysdir):
- m = re.search("(" + diskname + "\d+)", folder)
- if m:
- part = {}
- partname = m.group(1)
- part_sysdir = sysdir + "/" + partname
- part['start'] = get_file_content(part_sysdir + "/start",0)
- part['sectors'] = get_file_content(part_sysdir + "/size",0)
- part['sectorsize'] = get_file_content(part_sysdir + "/queue/physical_block_size")
- if not part['sectorsize']:
- part['sectorsize'] = get_file_content(part_sysdir + "/queue/hw_sector_size",512)
- part['size'] = module.pretty_bytes((float(part['sectors']) * float(part['sectorsize'])))
- d['partitions'][partname] = part
- d['rotational'] = get_file_content(sysdir + "/queue/rotational")
- d['scheduler_mode'] = ""
- scheduler = get_file_content(sysdir + "/queue/scheduler")
- if scheduler is not None:
- m = re.match(".*?(\[(.*)\])", scheduler)
- if m:
- d['scheduler_mode'] = m.group(2)
- d['sectors'] = get_file_content(sysdir + "/size")
- if not d['sectors']:
- d['sectors'] = 0
- d['sectorsize'] = get_file_content(sysdir + "/queue/physical_block_size")
- if not d['sectorsize']:
- d['sectorsize'] = get_file_content(sysdir + "/queue/hw_sector_size",512)
- d['size'] = module.pretty_bytes(float(d['sectors']) * float(d['sectorsize']))
- d['host'] = ""
- # domains are numbered (0 to ffff), bus (0 to ff), slot (0 to 1f), and function (0 to 7).
- m = re.match(".+/([a-f0-9]{4}:[a-f0-9]{2}:[0|1][a-f0-9]\.[0-7])/", sysdir)
- if m and pcidata:
- pciid = m.group(1)
- did = re.escape(pciid)
- m = re.search("^" + did + "\s(.*)$", pcidata, re.MULTILINE)
- d['host'] = m.group(1)
- d['holders'] = []
- if os.path.isdir(sysdir + "/holders"):
- for folder in os.listdir(sysdir + "/holders"):
- if not folder.startswith("dm-"):
- continue
- name = get_file_content(sysdir + "/holders/" + folder + "/dm/name")
- if name:
- d['holders'].append(name)
- else:
- d['holders'].append(folder)
- self.facts['devices'][diskname] = d
- class SunOSHardware(Hardware):
- """
- In addition to the generic memory and cpu facts, this also sets
- swap_reserved_mb and swap_allocated_mb that is available from *swap -s*.
- """
- platform = 'SunOS'
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- return self.facts
- def get_cpu_facts(self):
- physid = 0
- sockets = {}
- rc, out, err = module.run_command("/usr/bin/kstat cpu_info")
- self.facts['processor'] = []
- for line in out.split('\n'):
- if len(line) < 1:
- continue
- data = line.split(None, 1)
- key = data[0].strip()
- # "brand" works on Solaris 10 & 11. "implementation" for Solaris 9.
- if key == 'module:':
- brand = ''
- elif key == 'brand':
- brand = data[1].strip()
- elif key == 'clock_MHz':
- clock_mhz = data[1].strip()
- elif key == 'implementation':
- processor = brand or data[1].strip()
- # Add clock speed to description for SPARC CPU
- if self.facts['machine'] != 'i86pc':
- processor += " @ " + clock_mhz + "MHz"
- if 'processor' not in self.facts:
- self.facts['processor'] = []
- self.facts['processor'].append(processor)
- elif key == 'chip_id':
- physid = data[1].strip()
- if physid not in sockets:
- sockets[physid] = 1
- else:
- sockets[physid] += 1
- # Counting cores on Solaris can be complicated.
- # https://blogs.oracle.com/mandalika/entry/solaris_show_me_the_cpu
- # Treat 'processor_count' as physical sockets and 'processor_cores' as
- # virtual CPUs visisble to Solaris. Not a true count of cores for modern SPARC as
- # these processors have: sockets -> cores -> threads/virtual CPU.
- if len(sockets) > 0:
- self.facts['processor_count'] = len(sockets)
- self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
- else:
- self.facts['processor_cores'] = 'NA'
- self.facts['processor_count'] = len(self.facts['processor'])
- def get_memory_facts(self):
- rc, out, err = module.run_command(["/usr/sbin/prtconf"])
- for line in out.split('\n'):
- if 'Memory size' in line:
- self.facts['memtotal_mb'] = line.split()[2]
- rc, out, err = module.run_command("/usr/sbin/swap -s")
- allocated = long(out.split()[1][:-1])
- reserved = long(out.split()[5][:-1])
- used = long(out.split()[8][:-1])
- free = long(out.split()[10][:-1])
- self.facts['swapfree_mb'] = free / 1024
- self.facts['swaptotal_mb'] = (free + used) / 1024
- self.facts['swap_allocated_mb'] = allocated / 1024
- self.facts['swap_reserved_mb'] = reserved / 1024
- class OpenBSDHardware(Hardware):
- """
- OpenBSD-specific subclass of Hardware. Defines memory, CPU and device facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- - processor_speed
- - devices
- """
- platform = 'OpenBSD'
- DMESG_BOOT = '/var/run/dmesg.boot'
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.sysctl = self.get_sysctl()
- self.get_memory_facts()
- self.get_processor_facts()
- self.get_device_facts()
- return self.facts
- def get_sysctl(self):
- rc, out, err = module.run_command(["/sbin/sysctl", "hw"])
- if rc != 0:
- return dict()
- sysctl = dict()
- for line in out.splitlines():
- (key, value) = line.split('=')
- sysctl[key] = value.strip()
- return sysctl
- def get_memory_facts(self):
- # Get free memory. vmstat output looks like:
- # procs memory page disks traps cpu
- # r b w avm fre flt re pi po fr sr wd0 fd0 int sys cs us sy id
- # 0 0 0 47512 28160 51 0 0 0 0 0 1 0 116 89 17 0 1 99
- rc, out, err = module.run_command("/usr/bin/vmstat")
- if rc == 0:
- self.facts['memfree_mb'] = long(out.splitlines()[-1].split()[4]) / 1024
- self.facts['memtotal_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024
- # Get swapctl info. swapctl output looks like:
- # total: 69268 1K-blocks allocated, 0 used, 69268 available
- # And for older OpenBSD:
- # total: 69268k bytes allocated = 0k used, 69268k available
- rc, out, err = module.run_command("/sbin/swapctl -sk")
- if rc == 0:
- data = out.split()
- self.facts['swapfree_mb'] = long(data[-2].translate(None, "kmg")) / 1024
- self.facts['swaptotal_mb'] = long(data[1].translate(None, "kmg")) / 1024
- def get_processor_facts(self):
- processor = []
- dmesg_boot = get_file_content(OpenBSDHardware.DMESG_BOOT)
- if not dmesg_boot:
- rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
- i = 0
- for line in dmesg_boot.splitlines():
- if line.split(' ', 1)[0] == 'cpu%i:' % i:
- processor.append(line.split(' ', 1)[1])
- i = i + 1
- processor_count = i
- self.facts['processor'] = processor
- self.facts['processor_count'] = processor_count
- # I found no way to figure out the number of Cores per CPU in OpenBSD
- self.facts['processor_cores'] = 'NA'
- def get_device_facts(self):
- devices = []
- devices.extend(self.sysctl['hw.disknames'].split(','))
- self.facts['devices'] = devices
- class FreeBSDHardware(Hardware):
- """
- FreeBSD-specific subclass of Hardware. Defines memory and CPU facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- - devices
- """
- platform = 'FreeBSD'
- DMESG_BOOT = '/var/run/dmesg.boot'
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- self.get_dmi_facts()
- self.get_device_facts()
- try:
- self.get_mount_facts()
- except TimeoutError:
- pass
- return self.facts
- def get_cpu_facts(self):
- self.facts['processor'] = []
- rc, out, err = module.run_command("/sbin/sysctl -n hw.ncpu")
- self.facts['processor_count'] = out.strip()
- dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT)
- if not dmesg_boot:
- rc, dmesg_boot, err = module.run_command("/sbin/dmesg")
- for line in dmesg_boot.split('\n'):
- if 'CPU:' in line:
- cpu = re.sub(r'CPU:\s+', r"", line)
- self.facts['processor'].append(cpu.strip())
- if 'Logical CPUs per core' in line:
- self.facts['processor_cores'] = line.split()[4]
- def get_memory_facts(self):
- rc, out, err = module.run_command("/sbin/sysctl vm.stats")
- for line in out.split('\n'):
- data = line.split()
- if 'vm.stats.vm.v_page_size' in line:
- pagesize = long(data[1])
- if 'vm.stats.vm.v_page_count' in line:
- pagecount = long(data[1])
- if 'vm.stats.vm.v_free_count' in line:
- freecount = long(data[1])
- self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
- self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
- # Get swapinfo. swapinfo output looks like:
- # Device 1M-blocks Used Avail Capacity
- # /dev/ada0p3 314368 0 314368 0%
- #
- rc, out, err = module.run_command("/usr/sbin/swapinfo -m")
- lines = out.split('\n')
- if len(lines[-1]) == 0:
- lines.pop()
- data = lines[-1].split()
- self.facts['swaptotal_mb'] = data[1]
- self.facts['swapfree_mb'] = data[3]
- @timeout(10)
- def get_mount_facts(self):
- self.facts['mounts'] = []
- fstab = get_file_content('/etc/fstab')
- if fstab:
- for line in fstab.split('\n'):
- if line.startswith('#') or line.strip() == '':
- continue
- fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
- self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
- def get_device_facts(self):
- sysdir = '/dev'
- self.facts['devices'] = {}
- drives = re.compile('(ada?\d+|da\d+|a?cd\d+)') #TODO: rc, disks, err = module.run_command("/sbin/sysctl kern.disks")
- slices = re.compile('(ada?\d+s\d+\w*|da\d+s\d+\w*)')
- if os.path.isdir(sysdir):
- dirlist = sorted(os.listdir(sysdir))
- for device in dirlist:
- d = drives.match(device)
- if d:
- self.facts['devices'][d.group(1)] = []
- s = slices.match(device)
- if s:
- self.facts['devices'][d.group(1)].append(s.group(1))
- def get_dmi_facts(self):
- ''' learn dmi facts from system
- Use dmidecode executable if available'''
- # Fall back to using dmidecode, if available
- dmi_bin = module.get_bin_path('dmidecode')
- DMI_DICT = dict(
- bios_date='bios-release-date',
- bios_version='bios-version',
- form_factor='chassis-type',
- product_name='system-product-name',
- product_serial='system-serial-number',
- product_uuid='system-uuid',
- product_version='system-version',
- system_vendor='system-manufacturer'
- )
- for (k, v) in DMI_DICT.items():
- if dmi_bin is not None:
- (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v))
- if rc == 0:
- # Strip out commented lines (specific dmidecode output)
- self.facts[k] = ''.join([ line for line in out.split('\n') if not line.startswith('#') ])
- try:
- json.dumps(self.facts[k])
- except UnicodeDecodeError:
- self.facts[k] = 'NA'
- else:
- self.facts[k] = 'NA'
- else:
- self.facts[k] = 'NA'
- class NetBSDHardware(Hardware):
- """
- NetBSD-specific subclass of Hardware. Defines memory and CPU facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- - devices
- """
- platform = 'NetBSD'
- MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree']
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- try:
- self.get_mount_facts()
- except TimeoutError:
- pass
- return self.facts
- def get_cpu_facts(self):
- i = 0
- physid = 0
- sockets = {}
- if not os.access("/proc/cpuinfo", os.R_OK):
- return
- self.facts['processor'] = []
- for line in open("/proc/cpuinfo").readlines():
- data = line.split(":", 1)
- key = data[0].strip()
- # model name is for Intel arch, Processor (mind the uppercase P)
- # works for some ARM devices, like the Sheevaplug.
- if key == 'model name' or key == 'Processor':
- if 'processor' not in self.facts:
- self.facts['processor'] = []
- self.facts['processor'].append(data[1].strip())
- i += 1
- elif key == 'physical id':
- physid = data[1].strip()
- if physid not in sockets:
- sockets[physid] = 1
- elif key == 'cpu cores':
- sockets[physid] = int(data[1].strip())
- if len(sockets) > 0:
- self.facts['processor_count'] = len(sockets)
- self.facts['processor_cores'] = reduce(lambda x, y: x + y, sockets.values())
- else:
- self.facts['processor_count'] = i
- self.facts['processor_cores'] = 'NA'
- def get_memory_facts(self):
- if not os.access("/proc/meminfo", os.R_OK):
- return
- for line in open("/proc/meminfo").readlines():
- data = line.split(":", 1)
- key = data[0]
- if key in NetBSDHardware.MEMORY_FACTS:
- val = data[1].strip().split(' ')[0]
- self.facts["%s_mb" % key.lower()] = long(val) / 1024
- @timeout(10)
- def get_mount_facts(self):
- self.facts['mounts'] = []
- fstab = get_file_content('/etc/fstab')
- if fstab:
- for line in fstab.split('\n'):
- if line.startswith('#') or line.strip() == '':
- continue
- fields = re.sub(r'\s+',' ',line.rstrip('\n')).split()
- self.facts['mounts'].append({'mount': fields[1] , 'device': fields[0], 'fstype' : fields[2], 'options': fields[3]})
- class AIX(Hardware):
- """
- AIX-specific subclass of Hardware. Defines memory and CPU facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor (a list)
- - processor_cores
- - processor_count
- """
- platform = 'AIX'
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- self.get_dmi_facts()
- return self.facts
- def get_cpu_facts(self):
- self.facts['processor'] = []
- rc, out, err = module.run_command("/usr/sbin/lsdev -Cc processor")
- if out:
- i = 0
- for line in out.split('\n'):
- if 'Available' in line:
- if i == 0:
- data = line.split(' ')
- cpudev = data[0]
- i += 1
- self.facts['processor_count'] = int(i)
- rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a type")
- data = out.split(' ')
- self.facts['processor'] = data[1]
- rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a smt_threads")
- data = out.split(' ')
- self.facts['processor_cores'] = int(data[1])
- def get_memory_facts(self):
- pagesize = 4096
- rc, out, err = module.run_command("/usr/bin/vmstat -v")
- for line in out.split('\n'):
- data = line.split()
- if 'memory pages' in line:
- pagecount = long(data[0])
- if 'free pages' in line:
- freecount = long(data[0])
- self.facts['memtotal_mb'] = pagesize * pagecount / 1024 / 1024
- self.facts['memfree_mb'] = pagesize * freecount / 1024 / 1024
- # Get swapinfo. swapinfo output looks like:
- # Device 1M-blocks Used Avail Capacity
- # /dev/ada0p3 314368 0 314368 0%
- #
- rc, out, err = module.run_command("/usr/sbin/lsps -s")
- if out:
- lines = out.split('\n')
- data = lines[1].split()
- swaptotal_mb = long(data[0].rstrip('MB'))
- percused = int(data[1].rstrip('%'))
- self.facts['swaptotal_mb'] = swaptotal_mb
- self.facts['swapfree_mb'] = long(swaptotal_mb * ( 100 - percused ) / 100)
- def get_dmi_facts(self):
- rc, out, err = module.run_command("/usr/sbin/lsattr -El sys0 -a fwversion")
- data = out.split()
- self.facts['firmware_version'] = data[1].strip('IBM,')
- class HPUX(Hardware):
- """
- HP-UX-specifig subclass of Hardware. Defines memory and CPU facts:
- - memfree_mb
- - memtotal_mb
- - swapfree_mb
- - swaptotal_mb
- - processor
- - processor_cores
- - processor_count
- - model
- - firmware
- """
- platform = 'HP-UX'
- def __init__(self):
- Hardware.__init__(self)
- def populate(self):
- self.get_cpu_facts()
- self.get_memory_facts()
- self.get_hw_facts()
- return self.facts
- def get_cpu_facts(self):…