PageRenderTime 127ms CodeModel.GetById 15ms app.highlight 96ms RepoModel.GetById 1ms app.codeStats 2ms

/lib/ansible/module_utils/facts.py

https://github.com/ajanthanm/ansible
Python | 2371 lines | 2067 code | 133 blank | 171 comment | 243 complexity | a9c77d38aded62bddd620d444873d9fa MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file