saltstack/salt

View on GitHub
salt/grains/smartos.py

Summary

Maintainability
B
5 hrs
Test Coverage
# -*- coding: utf-8 -*-
'''
SmartOS grain provider

:maintainer:    Jorge Schrauwen <sjorge@blackdot.be>
:maturity:      new
:depends:       salt.utils, salt.ext.six, salt.module.cmdmod
:platform:      SmartOS

.. versionadded:: nitrogen

'''
from __future__ import absolute_import, print_function, unicode_literals

# Import python libs
import os
import re
import logging

# Import salt libs
import salt.utils.dictupdate
import salt.utils.json
import salt.utils.path
import salt.utils.platform
import salt.utils.stringutils
from salt.ext.six.moves import zip

# Solve the Chicken and egg problem where grains need to run before any
# of the modules are loaded and are generally available for any usage.
import salt.modules.cmdmod

__virtualname__ = 'smartos'
__salt__ = {
    'cmd.run': salt.modules.cmdmod.run,
}

log = logging.getLogger(__name__)


def __virtual__():
    '''
    Only load when we are on SmartOS
    '''
    if salt.utils.platform.is_smartos():
        return __virtualname__
    return False


def _smartos_computenode_data():
    '''
    Return useful information from a SmartOS compute node
    '''
    # Provides:
    #   vms_total
    #   vms_running
    #   vms_stopped
    #   vms_type
    #   sdc_version
    #   vm_capable
    #   vm_hw_virt

    grains = {}

    # collect vm data
    vms = {}
    for vm in __salt__['cmd.run']('vmadm list -p -o uuid,alias,state,type').split("\n"):
        vm = dict(list(zip(['uuid', 'alias', 'state', 'type'], vm.split(':'))))
        vms[vm['uuid']] = vm
        del vms[vm['uuid']]['uuid']

    # set vm grains
    grains['computenode_vms_total'] = len(vms)
    grains['computenode_vms_running'] = 0
    grains['computenode_vms_stopped'] = 0
    grains['computenode_vms_type'] = {'KVM': 0, 'LX': 0, 'OS': 0}
    for vm in vms:
        if vms[vm]['state'].lower() == 'running':
            grains['computenode_vms_running'] += 1
        elif vms[vm]['state'].lower() == 'stopped':
            grains['computenode_vms_stopped'] += 1

        if vms[vm]['type'] not in grains['computenode_vms_type']:
            # NOTE: be prepared for when bhyve gets its own type
            grains['computenode_vms_type'][vms[vm]['type']] = 0
        grains['computenode_vms_type'][vms[vm]['type']] += 1

    # sysinfo derived grains
    sysinfo = salt.utils.json.loads(__salt__['cmd.run']('sysinfo'))
    grains['computenode_sdc_version'] = sysinfo['SDC Version']
    grains['computenode_vm_capable'] = sysinfo['VM Capable']
    if sysinfo['VM Capable']:
        grains['computenode_vm_hw_virt'] = sysinfo['CPU Virtualization']

    # sysinfo derived smbios grains
    grains['manufacturer'] = sysinfo['Manufacturer']
    grains['productname'] = sysinfo['Product']
    grains['uuid'] = sysinfo['UUID']

    return grains


def _smartos_zone_data():
    '''
    Return useful information from a SmartOS zone
    '''
    # Provides:
    #   zoneid
    #   zonename
    #   imageversion
    grains = {}

    zoneinfo = __salt__['cmd.run']('zoneadm list -p').strip().split(":")
    grains["zoneid"] = zoneinfo[0]
    grains["zonename"] = zoneinfo[1]

    imageversion = re.compile('Image:\\s(.+)')
    grains["imageversion"] = "Unknown"
    if os.path.isfile('/etc/product'):
        with salt.utils.files.fopen('/etc/product', 'r') as fp_:
            for line in fp_:
                line = salt.utils.stringutils.to_unicode(line)
                match = imageversion.match(line)
                if match:
                    grains['imageversion'] = match.group(1)

    return grains


def _smartos_zone_pkgsrc_data():
    '''
    SmartOS zone pkgsrc information
    '''
    # Provides:
    #   pkgsrcversion
    #   pkgsrcpath

    grains = {
        'pkgsrcversion': 'Unknown',
        'pkgsrcpath': 'Unknown',
    }

    # NOTE: we are specifically interested in the SmartOS pkgsrc version and path
    #       - PKG_PATH MAY be different on non-SmartOS systems, but they will not
    #         use this grains module.
    #       - A sysadmin with advanced needs COULD create a 'spin' with a totally
    #         different URL. But at that point the value would be meaning less in
    #         the context of the pkgsrcversion grain as it will not followed the
    #         SmartOS pkgsrc versioning. So 'Unknown' would be appropriate.
    pkgsrcpath = re.compile('PKG_PATH=(.+)')
    pkgsrcversion = re.compile('^https?://pkgsrc.joyent.com/packages/SmartOS/(.+)/(.+)/All$')
    pkg_install_paths = [
        '/opt/local/etc/pkg_install.conf',
        '/opt/tools/etc/pkg_install.conf',
    ]
    for pkg_install in pkg_install_paths:
        if os.path.isfile(pkg_install):
            with salt.utils.files.fopen(pkg_install, 'r') as fp_:
                for line in fp_:
                    line = salt.utils.stringutils.to_unicode(line)
                    match_pkgsrcpath = pkgsrcpath.match(line)
                    if match_pkgsrcpath:
                        grains['pkgsrcpath'] = match_pkgsrcpath.group(1)
                        match_pkgsrcversion = pkgsrcversion.match(match_pkgsrcpath.group(1))
                        if match_pkgsrcversion:
                            grains['pkgsrcversion'] = match_pkgsrcversion.group(1)
                        break

    return grains


def _smartos_zone_pkgin_data():
    '''
    SmartOS zone pkgin information
    '''
    # Provides:
    #   pkgin_repositories

    grains = {
        'pkgin_repositories': [],
    }

    pkginrepo = re.compile('^(?:https|http|ftp|file)://.*$')
    repositories_path = [
        '/opt/local/etc/pkgin/repositories.conf',
        '/opt/tools/etc/pkgin/repositories.conf',
    ]
    for repositories in repositories_path:
        if os.path.isfile(repositories):
            with salt.utils.files.fopen(repositories, 'r') as fp_:
                for line in fp_:
                    line = salt.utils.stringutils.to_unicode(line).strip()
                    if pkginrepo.match(line):
                        grains['pkgin_repositories'].append(line)

    return grains


def smartos():
    '''
    Provide grains for SmartOS
    '''
    grains = {}

    if salt.utils.platform.is_smartos_zone():
        grains = salt.utils.dictupdate.update(grains, _smartos_zone_data(), merge_lists=True)
    elif salt.utils.platform.is_smartos_globalzone():
        grains = salt.utils.dictupdate.update(grains, _smartos_computenode_data(), merge_lists=True)
    grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgin_data(), merge_lists=True)
    grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgsrc_data(), merge_lists=True)

    return grains


# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4