<commit_before># -*- coding: utf-8 -*-
'''
Module for managing ext2/3/4 file systems
'''
from __future__ import absolute_import

# Import python libs
import logging

# Import salt libs
import salt.utils

log = logging.getLogger(__name__)

def __virtual__():
    '''
    Only work on POSIX-like systems
    '''
    if salt.utils.is_windows():
        return (False, 'The extfs execution module cannot be loaded: only available on non-Windows systems.')
    return True

def mkfs(device, fs_type, **kwargs):
    '''
    Create a file system on the specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.mkfs /dev/sda1 fs_type=ext4 opts='acl,noexec'

    Valid options are:

    * **block_size**: 1024, 2048 or 4096
    * **check**: check for bad blocks
    * **direct**: use direct IO
    * **ext_opts**: extended file system options (comma-separated)
    * **fragment_size**: size of fragments
    * **force**: setting force to True will cause mke2fs to specify the -F
      option twice (it is already set once); this is truly dangerous
    * **blocks_per_group**: number of blocks in a block group
    * **number_of_groups**: ext4 option for a virtual block group
    * **bytes_per_inode**: set the bytes/inode ratio
    * **inode_size**: size of the inode
    * **journal**: set to True to create a journal (default on ext3/4)
    * **journal_opts**: options for the fs journal (comma separated)
    * **blocks_file**: read bad blocks from file
    * **label**: label to apply to the file system
    * **reserved**: percentage of blocks reserved for super-user
    * **last_dir**: last mounted directory
    * **test**: set to True to not actually create the file system (mke2fs -n)
    * **number_of_inodes**: override default number of inodes
    * **creator_os**: override "creator operating system" field
    * **opts**: mount options (comma separated)
    * **revision**: set the filesystem revision (default 1)
    * **super**: write superblock and group descriptors only
    * **fs_type**: set the filesystem type (REQUIRED)
    * **usage_type**: how the filesystem is going to be used
    * **uuid**: set the UUID for the file system

    See the ``mke2fs(8)`` manpage for a more complete description of these
    options.
    '''
    kwarg_map = {'block_size': 'b',
                 'check': 'c',
                 'direct': 'D',
                 'ext_opts': 'E',
                 'fragment_size': 'f',
                 'force': 'F',
                 'blocks_per_group': 'g',
                 'number_of_groups': 'G',
                 'bytes_per_inode': 'i',
                 'inode_size': 'I',
                 'journal': 'j',
                 'journal_opts': 'J',
                 'blocks_file': 'l',
                 'label': 'L',
                 'reserved': 'm',
                 'last_dir': 'M',
                 'test': 'n',
                 'number_of_inodes': 'N',
                 'creator_os': 'o',
                 'opts': 'O',
                 'revision': 'r',
                 'super': 'S',
                 'usage_type': 'T',
                 'uuid': 'U'}

    opts = ''
    for key in kwargs:
        if key in kwarg_map:
            opt = kwarg_map[key]
            if kwargs[key] == 'True':
                opts += '-{0} '.format(opt)
            else:
                opts += '-{0} {1} '.format(opt, kwargs[key])
    cmd = 'mke2fs -F -t {0} {1}{2}'.format(fs_type, opts, device)
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    ret = []
    for line in out:
        if not line:
            continue
        elif line.startswith('mke2fs'):
            continue
        elif line.startswith('Discarding device blocks'):
            continue
        elif line.startswith('Allocating group tables'):
            continue
        elif line.startswith('Writing inode tables'):
            continue
        elif line.startswith('Creating journal'):
            continue
        elif line.startswith('Writing superblocks'):
            continue
        ret.append(line)
    return ret

def tune(device, **kwargs):
    '''
    Set attributes for the specified device (using tune2fs)

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.tune /dev/sda1 force=True label=wildstallyns opts='acl,noexec'

    Valid options are:

    * **max**: max mount count
    * **count**: mount count
    * **error**: error behavior
    * **extended_opts**: extended options (comma separated)
    * **force**: force, even if there are errors (set to True)
    * **group**: group name or gid that can use the reserved blocks
    * **interval**: interval between checks
    * **journal**: set to True to create a journal (default on ext3/4)
    * **journal_opts**: options for the fs journal (comma separated)
    * **label**: label to apply to the file system
    * **reserved**: percentage of blocks reserved for super-user
    * **last_dir**: last mounted directory
    * **opts**: mount options (comma separated)
    * **feature**: set or clear a feature (comma separated)
    * **mmp_check**: mmp check interval
    * **reserved**: reserved blocks count
    * **quota_opts**: quota options (comma separated)
    * **time**: time last checked
    * **user**: user or uid who can use the reserved blocks
    * **uuid**: set the UUID for the file system

    See the ``mke2fs(8)`` manpage for a more complete description of these
    options.
    '''
    kwarg_map = {'max': 'c',
                 'count': 'C',
                 'error': 'e',
                 'extended_opts': 'E',
                 'force': 'f',
                 'group': 'g',
                 'interval': 'i',
                 'journal': 'j',
                 'journal_opts': 'J',
                 'label': 'L',
                 'last_dir': 'M',
                 'opts': 'o',
                 'feature': 'O',
                 'mmp_check': 'p',
                 'reserved': 'r',
                 'quota_opts': 'Q',
                 'time': 'T',
                 'user': 'u',
                 'uuid': 'U'}
    opts = ''
    for key in kwargs:
        if key in kwarg_map:
            opt = kwarg_map[key]
            if kwargs[key] == 'True':
                opts += '-{0} '.format(opt)
            else:
                opts += '-{0} {1} '.format(opt, kwargs[key])
    cmd = 'tune2fs {0}{1}'.format(opts, device)
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    return out

def attributes(device, args=None):
    '''
    Return attributes from dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.attributes /dev/sda1
    '''
    fsdump = dump(device, args)
    return fsdump['attributes']

def blocks(device, args=None):
    '''
    Return block and inode info from dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.blocks /dev/sda1
    '''
    fsdump = dump(device, args)
    return fsdump['blocks']

def dump(device, args=None):
    '''
    Return all contents of dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.dump /dev/sda1
    '''
    cmd = 'dumpe2fs {0}'.format(device)
    if args:
        cmd = cmd + ' -' + args
    ret = {'attributes': {}, 'blocks': {}}
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    mode = 'opts'
    group = None
    for line in out:
        if not line:
            continue
        if line.startswith('dumpe2fs'):
            continue
        if mode == 'opts':
            line = line.replace('\t', ' ')
            comps = line.split(': ')
            if line.startswith('Filesystem features'):
                ret['attributes'][comps[0]] = comps[1].split()
            elif line.startswith('Group') and not line.startswith('Group descriptor size'):
                mode = 'blocks'
            else:
                ret['attributes'][comps[0]] = comps[1].strip()

        if mode == 'blocks':
            if line.startswith('Group'):
                line = line.replace(':', '')
                line = line.replace('(', '')
                line = line.replace(')', '')
                line = line.replace('[', '')
                line = line.replace(']', '')
                comps = line.split()
                blkgrp = comps[1]
                group = 'Group {0}'.format(blkgrp)
                ret['blocks'][group] = {}
                ret['blocks'][group]['group'] = blkgrp
                ret['blocks'][group]['range'] = comps[3]
                # TODO: comps[4:], which may look one one of the following:
                #     ITABLE_ZEROED
                #     INODE_UNINIT, ITABLE_ZEROED
                # Does anyone know what to call these?
                ret['blocks'][group]['extra'] = []
            elif 'Free blocks:' in line:
                comps = line.split(': ')
                free_blocks = comps[1].split(', ')
                ret['blocks'][group]['free blocks'] = free_blocks
            elif 'Free inodes:' in line:
                comps = line.split(': ')
                inodes = comps[1].split(', ')
                ret['blocks'][group]['free inodes'] = inodes
            else:
                line = line.strip()
                ret['blocks'][group]['extra'].append(line)
    return ret
<commit_msg>check count of columns after split<commit_after># -*- coding: utf-8 -*-
'''
Module for managing ext2/3/4 file systems
'''
from __future__ import absolute_import

# Import python libs
import logging

# Import salt libs
import salt.utils

log = logging.getLogger(__name__)

def __virtual__():
    '''
    Only work on POSIX-like systems
    '''
    if salt.utils.is_windows():
        return (False, 'The extfs execution module cannot be loaded: only available on non-Windows systems.')
    return True

def mkfs(device, fs_type, **kwargs):
    '''
    Create a file system on the specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.mkfs /dev/sda1 fs_type=ext4 opts='acl,noexec'

    Valid options are:

    * **block_size**: 1024, 2048 or 4096
    * **check**: check for bad blocks
    * **direct**: use direct IO
    * **ext_opts**: extended file system options (comma-separated)
    * **fragment_size**: size of fragments
    * **force**: setting force to True will cause mke2fs to specify the -F
      option twice (it is already set once); this is truly dangerous
    * **blocks_per_group**: number of blocks in a block group
    * **number_of_groups**: ext4 option for a virtual block group
    * **bytes_per_inode**: set the bytes/inode ratio
    * **inode_size**: size of the inode
    * **journal**: set to True to create a journal (default on ext3/4)
    * **journal_opts**: options for the fs journal (comma separated)
    * **blocks_file**: read bad blocks from file
    * **label**: label to apply to the file system
    * **reserved**: percentage of blocks reserved for super-user
    * **last_dir**: last mounted directory
    * **test**: set to True to not actually create the file system (mke2fs -n)
    * **number_of_inodes**: override default number of inodes
    * **creator_os**: override "creator operating system" field
    * **opts**: mount options (comma separated)
    * **revision**: set the filesystem revision (default 1)
    * **super**: write superblock and group descriptors only
    * **fs_type**: set the filesystem type (REQUIRED)
    * **usage_type**: how the filesystem is going to be used
    * **uuid**: set the UUID for the file system

    See the ``mke2fs(8)`` manpage for a more complete description of these
    options.
    '''
    kwarg_map = {'block_size': 'b',
                 'check': 'c',
                 'direct': 'D',
                 'ext_opts': 'E',
                 'fragment_size': 'f',
                 'force': 'F',
                 'blocks_per_group': 'g',
                 'number_of_groups': 'G',
                 'bytes_per_inode': 'i',
                 'inode_size': 'I',
                 'journal': 'j',
                 'journal_opts': 'J',
                 'blocks_file': 'l',
                 'label': 'L',
                 'reserved': 'm',
                 'last_dir': 'M',
                 'test': 'n',
                 'number_of_inodes': 'N',
                 'creator_os': 'o',
                 'opts': 'O',
                 'revision': 'r',
                 'super': 'S',
                 'usage_type': 'T',
                 'uuid': 'U'}

    opts = ''
    for key in kwargs:
        if key in kwarg_map:
            opt = kwarg_map[key]
            if kwargs[key] == 'True':
                opts += '-{0} '.format(opt)
            else:
                opts += '-{0} {1} '.format(opt, kwargs[key])
    cmd = 'mke2fs -F -t {0} {1}{2}'.format(fs_type, opts, device)
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    ret = []
    for line in out:
        if not line:
            continue
        elif line.startswith('mke2fs'):
            continue
        elif line.startswith('Discarding device blocks'):
            continue
        elif line.startswith('Allocating group tables'):
            continue
        elif line.startswith('Writing inode tables'):
            continue
        elif line.startswith('Creating journal'):
            continue
        elif line.startswith('Writing superblocks'):
            continue
        ret.append(line)
    return ret

def tune(device, **kwargs):
    '''
    Set attributes for the specified device (using tune2fs)

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.tune /dev/sda1 force=True label=wildstallyns opts='acl,noexec'

    Valid options are:

    * **max**: max mount count
    * **count**: mount count
    * **error**: error behavior
    * **extended_opts**: extended options (comma separated)
    * **force**: force, even if there are errors (set to True)
    * **group**: group name or gid that can use the reserved blocks
    * **interval**: interval between checks
    * **journal**: set to True to create a journal (default on ext3/4)
    * **journal_opts**: options for the fs journal (comma separated)
    * **label**: label to apply to the file system
    * **reserved**: percentage of blocks reserved for super-user
    * **last_dir**: last mounted directory
    * **opts**: mount options (comma separated)
    * **feature**: set or clear a feature (comma separated)
    * **mmp_check**: mmp check interval
    * **reserved**: reserved blocks count
    * **quota_opts**: quota options (comma separated)
    * **time**: time last checked
    * **user**: user or uid who can use the reserved blocks
    * **uuid**: set the UUID for the file system

    See the ``mke2fs(8)`` manpage for a more complete description of these
    options.
    '''
    kwarg_map = {'max': 'c',
                 'count': 'C',
                 'error': 'e',
                 'extended_opts': 'E',
                 'force': 'f',
                 'group': 'g',
                 'interval': 'i',
                 'journal': 'j',
                 'journal_opts': 'J',
                 'label': 'L',
                 'last_dir': 'M',
                 'opts': 'o',
                 'feature': 'O',
                 'mmp_check': 'p',
                 'reserved': 'r',
                 'quota_opts': 'Q',
                 'time': 'T',
                 'user': 'u',
                 'uuid': 'U'}
    opts = ''
    for key in kwargs:
        if key in kwarg_map:
            opt = kwarg_map[key]
            if kwargs[key] == 'True':
                opts += '-{0} '.format(opt)
            else:
                opts += '-{0} {1} '.format(opt, kwargs[key])
    cmd = 'tune2fs {0}{1}'.format(opts, device)
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    return out

def attributes(device, args=None):
    '''
    Return attributes from dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.attributes /dev/sda1
    '''
    fsdump = dump(device, args)
    return fsdump['attributes']

def blocks(device, args=None):
    '''
    Return block and inode info from dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.blocks /dev/sda1
    '''
    fsdump = dump(device, args)
    return fsdump['blocks']

def dump(device, args=None):
    '''
    Return all contents of dumpe2fs for a specified device

    CLI Example:

    .. code-block:: bash

        salt '*' extfs.dump /dev/sda1
    '''
    cmd = 'dumpe2fs {0}'.format(device)
    if args:
        cmd = cmd + ' -' + args
    ret = {'attributes': {}, 'blocks': {}}
    out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
    mode = 'opts'
    group = None
    for line in out:
        if not line:
            continue
        if line.startswith('dumpe2fs'):
            continue
        if mode == 'opts':
            line = line.replace('\t', ' ')
            comps = line.split(': ')
            if line.startswith('Filesystem features'):
                ret['attributes'][comps[0]] = comps[1].split()
            elif line.startswith('Group') and not line.startswith('Group descriptor size'):
                mode = 'blocks'
            else:
                if len(comps) < 2:
                    continue
                ret['attributes'][comps[0]] = comps[1].strip()

        if mode == 'blocks':
            if line.startswith('Group'):
                line = line.replace(':', '')
                line = line.replace('(', '')
                line = line.replace(')', '')
                line = line.replace('[', '')
                line = line.replace(']', '')
                comps = line.split()
                blkgrp = comps[1]
                group = 'Group {0}'.format(blkgrp)
                ret['blocks'][group] = {}
                ret['blocks'][group]['group'] = blkgrp
                ret['blocks'][group]['range'] = comps[3]
                # TODO: comps[4:], which may look one one of the following:
                #     ITABLE_ZEROED
                #     INODE_UNINIT, ITABLE_ZEROED
                # Does anyone know what to call these?
                ret['blocks'][group]['extra'] = []
            elif 'Free blocks:' in line:
                comps = line.split(': ')
                free_blocks = comps[1].split(', ')
                ret['blocks'][group]['free blocks'] = free_blocks
            elif 'Free inodes:' in line:
                comps = line.split(': ')
                inodes = comps[1].split(', ')
                ret['blocks'][group]['free inodes'] = inodes
            else:
                line = line.strip()
                ret['blocks'][group]['extra'].append(line)
    return ret
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Manage the information in the hosts file
'''

# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import os

# Import salt libs
import salt.utils.files
import salt.utils.odict as odict
import salt.utils.stringutils

# Import 3rd-party libs
from salt.ext import six
from salt.ext.six.moves import range  # pylint: disable=import-error,no-name-in-module,redefined-builtin

# pylint: disable=C0103
def __get_hosts_filename():
    '''
    Return the path to the appropriate hosts file
    '''
    return __salt__['config.option']('hosts.file')

def _get_or_create_hostfile():
    '''
    Wrapper of __get_hosts_filename but create host file if it
    does not exist.
    '''
    hfn = __get_hosts_filename()
    if hfn is None:
        hfn = ''
    if not os.path.exists(hfn):
        with salt.utils.files.fopen(hfn, 'w'):
            pass
    return hfn

def _list_hosts():
    '''
    Return the hosts found in the hosts file in as an OrderedDict
    '''
    count = 0
    hfn = __get_hosts_filename()
    ret = odict.OrderedDict()
    if not os.path.isfile(hfn):
        return ret
    with salt.utils.files.fopen(hfn) as ifile:
        for line in ifile:
            line = salt.utils.stringutils.to_unicode(line).strip()
            if not line:
                continue
            if line.startswith('#'):
                ret.setdefault('comment-{0}'.format(count), []).append(line)
                count += 1
                continue
            if '#' in line:
                line = line[:line.index('#')].strip()
            comps = line.split()
            ip = comps.pop(0)
            ret.setdefault(ip, []).extend(comps)
    return ret

def list_hosts():
    '''
    Return the hosts found in the hosts file in this format::

        {'<ip addr>': ['alias1', 'alias2', ...]}

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.list_hosts
    '''
    # msgpack does not like OrderedDict's
    return dict(_list_hosts())

def get_ip(host):
    '''
    Return the ip associated with the named host

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.get_ip <hostname>
    '''
    hosts = _list_hosts()
    if not hosts:
        return ''
    # Look for the op
    for addr in hosts:
        if host in hosts[addr]:
            return addr
    # ip not found
    return ''

def get_alias(ip):
    '''
    Return the list of aliases associated with an ip

    Aliases (host names) are returned in the order in which they
    appear in the hosts file.  If there are no aliases associated with
    the IP, an empty list is returned.

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.get_alias <ip addr>
    '''
    hosts = _list_hosts()
    if ip in hosts:
        return hosts[ip]
    return []

def has_pair(ip, alias):
    '''
    Return true if the alias is set

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.has_pair <ip> <alias>
    '''
    hosts = _list_hosts()
    return ip in hosts and alias in hosts[ip]

def set_host(ip, alias):
    '''
    Set the host entry in the hosts file for the given ip, this will overwrite
    any previous entry for the given ip

    .. versionchanged:: 2016.3.0
        If ``alias`` does not include any host names (it is the empty
        string or contains only whitespace), all entries for the given
        IP address are removed.

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.set_host <ip> <alias>
    '''
    hfn = _get_or_create_hostfile()
    ovr = False
    if not os.path.isfile(hfn):
        return False

    line_to_add = salt.utils.stringutils.to_bytes(
        ip + '\t\t' + alias + os.linesep
    )
    # support removing a host entry by providing an empty string
    if not alias.strip():
        line_to_add = b''

    with salt.utils.files.fopen(hfn, 'rb') as fp_:
        lines = fp_.readlines()
    for ind, _ in enumerate(lines):
        tmpline = lines[ind].strip()
        if not tmpline:
            continue
        if tmpline.startswith(b'#'):
            continue
        comps = tmpline.split()
        if comps[0] == salt.utils.stringutils.to_bytes(ip):
            if not ovr:
                lines[ind] = line_to_add
                ovr = True
            else:  # remove other entries
                lines[ind] = b''
    linesep_bytes = salt.utils.stringutils.to_bytes(os.linesep)
    if not ovr:
        # make sure there is a newline
        if lines and not lines[-1].endswith(linesep_bytes):
            lines[-1] += linesep_bytes
        line = line_to_add
        lines.append(line)
    with salt.utils.files.fopen(hfn, 'wb') as ofile:
        ofile.writelines(lines)
    return True

def rm_host(ip, alias):
    '''
    Remove a host entry from the hosts file

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.rm_host <ip> <alias>
    '''
    if not has_pair(ip, alias):
        return True
    hfn = _get_or_create_hostfile()
    with salt.utils.files.fopen(hfn, 'rb') as fp_:
        lines = fp_.readlines()
    for ind, _ in enumerate(lines):
        tmpline = lines[ind].strip()
        if not tmpline:
            continue
        if tmpline.startswith(b'#'):
            continue
        comps = salt.utils.stringutils.to_unicode(tmpline).split()
        if comps[0] == ip:
            newline = '{0}\t\t'.format(comps[0])
            for existing in comps[1:]:
                if existing == alias:
                    continue
                newline += ' {0}'.format(existing)
            if newline.strip() == ip:
                # No aliases exist for the line, make it empty
                lines[ind] = b''
            else:
                # Only an alias was removed
                lines[ind] = newline + os.linesep
    with salt.utils.files.fopen(hfn, 'wb') as ofile:
        ofile.writelines(lines)
    return True

def add_host(ip, alias):
    '''
    Add a host to an existing entry, if the entry is not in place then create
    it with the given host

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.add_host <ip> <alias>
    '''
    hfn = _get_or_create_hostfile()
    if not os.path.isfile(hfn):
        return False

    if has_pair(ip, alias):
        return True

    hosts = _list_hosts()
    inserted = False
    for i, h in six.iteritems(hosts):
        for j in range(len(h)):
            if h[j].startswith('#') and i == ip:
                h.insert(j, alias)
                inserted = True
    if not inserted:
        hosts.setdefault(ip, []).append(alias)
    _write_hosts(hosts)
    return True

def _write_hosts(hosts):
    lines = []
    for ip, aliases in six.iteritems(hosts):
        if ip:
            if ip.startswith('comment'):
                line = ''.join(aliases)
            else:
                line = '{0}\t\t{1}'.format(
                    ip,
                    ' '.join(aliases)
                    )
        lines.append(line)

    hfn = _get_or_create_hostfile()
    with salt.utils.files.fopen(hfn, 'w+') as ofile:
        for line in lines:
            if line.strip():
                # /etc/hosts needs to end with a newline so that some utils
                # that read it do not break
                ofile.write(salt.utils.stringutils.to_str(
                    line.strip() + six.text_type(os.linesep)
                ))
<commit_msg>fix issue#49150 hosts.rm_host changes /etc/hosts with odd characters<commit_after># -*- coding: utf-8 -*-
'''
Manage the information in the hosts file
'''

# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import os

# Import salt libs
import salt.utils.files
import salt.utils.odict as odict
import salt.utils.stringutils

# Import 3rd-party libs
from salt.ext import six
from salt.ext.six.moves import range  # pylint: disable=import-error,no-name-in-module,redefined-builtin

# pylint: disable=C0103
def __get_hosts_filename():
    '''
    Return the path to the appropriate hosts file
    '''
    return __salt__['config.option']('hosts.file')

def _get_or_create_hostfile():
    '''
    Wrapper of __get_hosts_filename but create host file if it
    does not exist.
    '''
    hfn = __get_hosts_filename()
    if hfn is None:
        hfn = ''
    if not os.path.exists(hfn):
        with salt.utils.files.fopen(hfn, 'w'):
            pass
    return hfn

def _list_hosts():
    '''
    Return the hosts found in the hosts file in as an OrderedDict
    '''
    count = 0
    hfn = __get_hosts_filename()
    ret = odict.OrderedDict()
    if not os.path.isfile(hfn):
        return ret
    with salt.utils.files.fopen(hfn) as ifile:
        for line in ifile:
            line = salt.utils.stringutils.to_unicode(line).strip()
            if not line:
                continue
            if line.startswith('#'):
                ret.setdefault('comment-{0}'.format(count), []).append(line)
                count += 1
                continue
            if '#' in line:
                line = line[:line.index('#')].strip()
            comps = line.split()
            ip = comps.pop(0)
            ret.setdefault(ip, []).extend(comps)
    return ret

def list_hosts():
    '''
    Return the hosts found in the hosts file in this format::

        {'<ip addr>': ['alias1', 'alias2', ...]}

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.list_hosts
    '''
    # msgpack does not like OrderedDict's
    return dict(_list_hosts())

def get_ip(host):
    '''
    Return the ip associated with the named host

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.get_ip <hostname>
    '''
    hosts = _list_hosts()
    if not hosts:
        return ''
    # Look for the op
    for addr in hosts:
        if host in hosts[addr]:
            return addr
    # ip not found
    return ''

def get_alias(ip):
    '''
    Return the list of aliases associated with an ip

    Aliases (host names) are returned in the order in which they
    appear in the hosts file.  If there are no aliases associated with
    the IP, an empty list is returned.

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.get_alias <ip addr>
    '''
    hosts = _list_hosts()
    if ip in hosts:
        return hosts[ip]
    return []

def has_pair(ip, alias):
    '''
    Return true if the alias is set

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.has_pair <ip> <alias>
    '''
    hosts = _list_hosts()
    return ip in hosts and alias in hosts[ip]

def set_host(ip, alias):
    '''
    Set the host entry in the hosts file for the given ip, this will overwrite
    any previous entry for the given ip

    .. versionchanged:: 2016.3.0
        If ``alias`` does not include any host names (it is the empty
        string or contains only whitespace), all entries for the given
        IP address are removed.

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.set_host <ip> <alias>
    '''
    hfn = _get_or_create_hostfile()
    ovr = False
    if not os.path.isfile(hfn):
        return False

    line_to_add = salt.utils.stringutils.to_bytes(
        ip + '\t\t' + alias + os.linesep
    )
    # support removing a host entry by providing an empty string
    if not alias.strip():
        line_to_add = b''

    with salt.utils.files.fopen(hfn, 'rb') as fp_:
        lines = fp_.readlines()
    for ind, _ in enumerate(lines):
        tmpline = lines[ind].strip()
        if not tmpline:
            continue
        if tmpline.startswith(b'#'):
            continue
        comps = tmpline.split()
        if comps[0] == salt.utils.stringutils.to_bytes(ip):
            if not ovr:
                lines[ind] = line_to_add
                ovr = True
            else:  # remove other entries
                lines[ind] = b''
    linesep_bytes = salt.utils.stringutils.to_bytes(os.linesep)
    if not ovr:
        # make sure there is a newline
        if lines and not lines[-1].endswith(linesep_bytes):
            lines[-1] += linesep_bytes
        line = line_to_add
        lines.append(line)
    with salt.utils.files.fopen(hfn, 'wb') as ofile:
        ofile.writelines(lines)
    return True

def rm_host(ip, alias):
    '''
    Remove a host entry from the hosts file

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.rm_host <ip> <alias>
    '''
    if not has_pair(ip, alias):
        return True
    hfn = _get_or_create_hostfile()
    with salt.utils.files.fopen(hfn, 'rb') as fp_:
        lines = fp_.readlines()
    for ind, _ in enumerate(lines):
        tmpline = lines[ind].strip()
        if not tmpline:
            continue
        if tmpline.startswith(b'#'):
            continue
        comps = tmpline.split()
        b_ip = salt.utils.stringutils.to_bytes(ip)
        b_alias = salt.utils.stringutils.to_bytes(alias)
        if comps[0] == b_ip:
            newline = comps[0] + b'\t\t'
            for existing in comps[1:]:
                if existing == b_alias:
                    continue
                newline += existing + b' '
            if newline.strip() == b_ip:
                # No aliases exist for the line, make it empty
                lines[ind] = b''
            else:
                # Only an alias was removed
                lines[ind] = newline + salt.utils.stringutils.to_bytes(os.linesep)
    with salt.utils.files.fopen(hfn, 'wb') as ofile:
        ofile.writelines(lines)
    return True

def add_host(ip, alias):
    '''
    Add a host to an existing entry, if the entry is not in place then create
    it with the given host

    CLI Example:

    .. code-block:: bash

        salt '*' hosts.add_host <ip> <alias>
    '''
    hfn = _get_or_create_hostfile()
    if not os.path.isfile(hfn):
        return False

    if has_pair(ip, alias):
        return True

    hosts = _list_hosts()
    inserted = False
    for i, h in six.iteritems(hosts):
        for j in range(len(h)):
            if h[j].startswith('#') and i == ip:
                h.insert(j, alias)
                inserted = True
    if not inserted:
        hosts.setdefault(ip, []).append(alias)
    _write_hosts(hosts)
    return True

def _write_hosts(hosts):
    lines = []
    for ip, aliases in six.iteritems(hosts):
        if ip:
            if ip.startswith('comment'):
                line = ''.join(aliases)
            else:
                line = '{0}\t\t{1}'.format(
                    ip,
                    ' '.join(aliases)
                    )
        lines.append(line)

    hfn = _get_or_create_hostfile()
    with salt.utils.files.fopen(hfn, 'w+') as ofile:
        for line in lines:
            if line.strip():
                # /etc/hosts needs to end with a newline so that some utils
                # that read it do not break
                ofile.write(salt.utils.stringutils.to_str(
                    line.strip() + six.text_type(os.linesep)
                ))
<|endoftext|>


<commit_before># Copyright (c) 2016 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

import pytest
import os.path

import UM.Settings
import UM.PluginObject
import UM.PluginRegistry
import UM.Settings.DefinitionContainer
import UM.Settings.InstanceContainer
import UM.Settings.ContainerStack

from UM.Resources import Resources
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase

##  Fake container class to add to the container registry.
#
#   This allows us to test the container registry without testing the container
#   class. If something is wrong in the container class it won't influence this
#   test.
class MockContainer(UM.Settings.ContainerInterface.ContainerInterface, UM.PluginObject.PluginObject):
    ##  Initialise a new definition container.
    #
    #   The container will have the specified ID and all metadata in the
    #   provided dictionary.
    def __init__(self, id, metadata):
        self._id = id
        self._metadata = metadata

    ##  Gets the ID that was provided at initialisation.
    #
    #   \return The ID of the container.
    def getId(self):
        return self._id

    ##  Gets all metadata of this container.
    #
    #   This returns the metadata dictionary that was provided in the
    #   constructor of this mock container.
    #
    #   \return The metadata for this container.
    def getMetaData(self):
        return self._metadata

    ##  Gets a metadata entry from the metadata dictionary.
    #
    #   \param key The key of the metadata entry.
    #   \return The value of the metadata entry, or None if there is no such
    #   entry.
    def getMetaDataEntry(self, entry, default = None):
        if entry in self._metadata:
            return self._metadata[entry]
        return default

    ##  Gets a human-readable name for this container.
    #
    #   \return Always returns "MockContainer".
    def getName(self):
        return "MockContainer"

    ##  Get the value of a container item.
    #
    #   Since this mock container cannot contain any items, it always returns
    #   None.
    #
    #   \return Always returns None.
    def getValue(self, key):
        pass

    ##  Serializes the container to a string representation.
    #
    #   This method is not implemented in the mock container.
    def serialize(self):
        raise NotImplementedError()

    ##  Deserializes the container from a string representation.
    #
    #   This method is not implemented in the mock container.
    def deserialize(self, serialized):
        raise NotImplementedError()

##  Creates a brand new container registry.
#
#   To force a new container registry, the registry is first set to None and
#   then re-requested.
#
#   \return A brand new container registry.
@pytest.fixture
def container_registry():
    Resources.addSearchPath(os.path.dirname(os.path.abspath(__file__)))
    UM.Settings.ContainerRegistry._ContainerRegistry__instance = None # Reset the private instance variable every time
    UM.PluginRegistry.PluginRegistry.getInstance().removeType("settings_container")

    return UM.Settings.ContainerRegistry.getInstance()

##  Tests adding a container to the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_addContainer(container_registry):
    definition_container_0 = UM.Settings.DefinitionContainer("a", {})
    container_registry.addContainer(definition_container_0)
    assert container_registry.findDefinitionContainers() == [definition_container_0]

    # Add a second one of the same type.
    definition_container_1 = UM.Settings.DefinitionContainer("b", {})
    container_registry.addContainer(definition_container_1)
    assert container_registry.findDefinitionContainers(id = "b") == [definition_container_1] # Test in two queries, since the list order doesn't matter.
    assert container_registry.findDefinitionContainers(id = "a") == [definition_container_0]

    # Add a container with the same type and same ID.
    definition_container_1_clone = UM.Settings.DefinitionContainer("b", {})
    container_registry.addContainer(definition_container_1_clone)
    assert container_registry.findDefinitionContainers(id = "b") == [definition_container_1] # Didn't get added!

    # For good measure, add a container with a different type too.
    instance_container_1 = UM.Settings.InstanceContainer("a")
    container_registry.addContainer(instance_container_1)
    assert container_registry.findInstanceContainers() == [instance_container_1]

##  Tests adding a container type to the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_addContainerType(container_registry):
    container_registry.addContainerType(MockContainer("a", {} )) # Test if it doesn't crash.
    # Actually testing the result can only be done with the load function, so refer to test_load for that.
    with pytest.raises(Exception):
        container_registry.addContainerType(None)

##  Tests the creation of the container registry.
#
#   This is tested using the fixture to create a container registry.
#
#   \param container_registry A newly created container registry instance, from
#   a fixture.
def test_create(container_registry):
    assert container_registry != None

##  Individual test cases for test_findDefinitionContainers as well as
#   test_findInstanceContainers.
#
#   Each entry has a descriptive name for debugging.
#   Each entry also has a list of "containers", each of which is an arbitrary
#   dictionary of metadata. Each dictionary MUST have an ID entry, which will be
#   taken out and used as ID for the container.
#   Each entry also has a "filter" dictionary, which is used as filter for the
#   metadata of the containers.
#   Each entry also has an "answer", which is a list of dictionaries again
#   representing the containers that must be returned by the search.
test_findContainers_data = [
    {
        "name": "No containers",
        "containers": [

        ],
        "filter": { "id": "a" },
        "result": [
            # Empty.
        ]
    },
    {
        "name": "Single ID match",
        "containers": [
            { "id": "a" },
            { "id": "b" }
        ],
        "filter": { "id": "a" },
        "result": [
            { "id": "a" }
        ]
    },
    {
        "name": "Double ID match",
        "containers": [
            { "id": "a" },
            { "id": "b" },
            { "id": "a" }
        ],
        "filter": { "id": "a" },
        "result": [
            { "id": "a" },
            { "id": "a" }
        ]
    },
    {
        "name": "Multiple constraints",
        "containers": [
            { "id": "a", "number": 1, "bool": False },
            { "id": "b", "number": 2, "bool": False },
            { "id": "c", "number": 2, "bool": True },
            { "id": "d", "number": 2, "bool": False }
        ],
        "filter": { "number": 2, "bool": False },
        "result": [
            { "id": "b", "number": 2, "bool": False },
            { "id": "d", "number": 2, "bool": False }
        ]
    },
    {
        "name": "Mixed Type",
        "containers": [
            { "id": "a", "number": 1, "mixed": "z" },
            { "id": "b", "number": 1, "mixed": 9 },
            { "id": "c", "number": 2, "mixed": 9 }
        ],
        "filter": { "number": 1, "mixed": 9 },
        "result": [
            { "id": "b", "number": 1, "mixed": 9 }
        ]
    }
]

##  Tests the findDefinitionContainers function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findDefinitionContainers(container_registry, data):
    for container in data["containers"]: # Fill the registry with mock containers.
        container_id = container["id"]
        definition_container = UM.Settings.DefinitionContainer(container_id, container)
        container_registry.addContainer(definition_container)

    results = container_registry.findDefinitionContainers(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the findInstanceContainers function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findInstanceContainers(container_registry, data):
    for container in data["containers"]: # Fill the registry with mock containers.
        container_id = container["id"]
        del container["id"]
        instance_container = UM.Settings.InstanceContainer(container_id, container)
        container_registry.addContainer(instance_container)

    results = container_registry.findInstanceContainers(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the findContainerStacks function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findContainerStacks(container_registry, data):
    for container in data["containers"]: # Fill the registry with container stacks.
        container_id = container["id"]
        del container["id"]
        container_stack = UM.Settings.ContainerStack(container_id, container)
        container_registry.addContainer(container_stack)

    results = container_registry.findContainerStacks(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the loading of containers into the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_load(container_registry):
    container_registry.load()

    definitions = container_registry.findDefinitionContainers(id = "single_setting")
    assert len(definitions) == 1

    definition = definitions[0]
    assert definition.getId() == "single_setting"

    definitions = container_registry.findDefinitionContainers(author = "Ultimaker")
    assert len(definitions) == 3

    ids_found = []
    for definition in definitions:
        ids_found.append(definition.getId())

    assert "metadata" in ids_found
    assert "single_setting" in ids_found
    assert "inherits" in ids_found

##  Helper function to verify if the metadata of the answers matches required
#   metadata.
#
#   This basically compares two sets. They are provided as two lists, but the
#   order doesn't matter.
#
#   \param answer A list of containers, each of which has metadata.
#   \param ground_truth A list of dictionaries, describing the metadata of each
#   required container.
def _verifyMetaDataMatches(answer, ground_truth):
    assert len(answer) == len(ground_truth)

    matches = 0
    for result in answer: # Go through all results and match them with our expected data.
        for required in list(ground_truth): # Iterate over a copy of the list so we do not modify the original data.
            if "id" in required: # Special casing for ID since that is not in the metadata.
                if result.getId() != required["id"]:
                    continue # No match.
                del required["id"] # Remove ID from the expected metadata since it is not part of the metadata.

            if result.getMetaData() == required:
                # If the metadata matches, we know this entry is valid.
                # Note that this requires specifying all metadata in the expected results.
                matches += 1
                break # We have a valid match.
    assert matches == len(ground_truth)<commit_msg>Test with copies of test data<commit_after># Copyright (c) 2016 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

import pytest
import os.path

import UM.Settings
import UM.PluginObject
import UM.PluginRegistry
import UM.Settings.DefinitionContainer
import UM.Settings.InstanceContainer
import UM.Settings.ContainerStack

from UM.Resources import Resources
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase

##  Fake container class to add to the container registry.
#
#   This allows us to test the container registry without testing the container
#   class. If something is wrong in the container class it won't influence this
#   test.
class MockContainer(UM.Settings.ContainerInterface.ContainerInterface, UM.PluginObject.PluginObject):
    ##  Initialise a new definition container.
    #
    #   The container will have the specified ID and all metadata in the
    #   provided dictionary.
    def __init__(self, id, metadata):
        self._id = id
        self._metadata = metadata

    ##  Gets the ID that was provided at initialisation.
    #
    #   \return The ID of the container.
    def getId(self):
        return self._id

    ##  Gets all metadata of this container.
    #
    #   This returns the metadata dictionary that was provided in the
    #   constructor of this mock container.
    #
    #   \return The metadata for this container.
    def getMetaData(self):
        return self._metadata

    ##  Gets a metadata entry from the metadata dictionary.
    #
    #   \param key The key of the metadata entry.
    #   \return The value of the metadata entry, or None if there is no such
    #   entry.
    def getMetaDataEntry(self, entry, default = None):
        if entry in self._metadata:
            return self._metadata[entry]
        return default

    ##  Gets a human-readable name for this container.
    #
    #   \return Always returns "MockContainer".
    def getName(self):
        return "MockContainer"

    ##  Get the value of a container item.
    #
    #   Since this mock container cannot contain any items, it always returns
    #   None.
    #
    #   \return Always returns None.
    def getValue(self, key):
        pass

    ##  Serializes the container to a string representation.
    #
    #   This method is not implemented in the mock container.
    def serialize(self):
        raise NotImplementedError()

    ##  Deserializes the container from a string representation.
    #
    #   This method is not implemented in the mock container.
    def deserialize(self, serialized):
        raise NotImplementedError()

##  Creates a brand new container registry.
#
#   To force a new container registry, the registry is first set to None and
#   then re-requested.
#
#   \return A brand new container registry.
@pytest.fixture
def container_registry():
    Resources.addSearchPath(os.path.dirname(os.path.abspath(__file__)))
    UM.Settings.ContainerRegistry._ContainerRegistry__instance = None # Reset the private instance variable every time
    UM.PluginRegistry.PluginRegistry.getInstance().removeType("settings_container")

    return UM.Settings.ContainerRegistry.getInstance()

##  Tests adding a container to the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_addContainer(container_registry):
    definition_container_0 = UM.Settings.DefinitionContainer("a", {})
    container_registry.addContainer(definition_container_0)
    assert container_registry.findDefinitionContainers() == [definition_container_0]

    # Add a second one of the same type.
    definition_container_1 = UM.Settings.DefinitionContainer("b", {})
    container_registry.addContainer(definition_container_1)
    assert container_registry.findDefinitionContainers(id = "b") == [definition_container_1] # Test in two queries, since the list order doesn't matter.
    assert container_registry.findDefinitionContainers(id = "a") == [definition_container_0]

    # Add a container with the same type and same ID.
    definition_container_1_clone = UM.Settings.DefinitionContainer("b", {})
    container_registry.addContainer(definition_container_1_clone)
    assert container_registry.findDefinitionContainers(id = "b") == [definition_container_1] # Didn't get added!

    # For good measure, add a container with a different type too.
    instance_container_1 = UM.Settings.InstanceContainer("a")
    container_registry.addContainer(instance_container_1)
    assert container_registry.findInstanceContainers() == [instance_container_1]

##  Tests adding a container type to the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_addContainerType(container_registry):
    container_registry.addContainerType(MockContainer("a", {} )) # Test if it doesn't crash.
    # Actually testing the result can only be done with the load function, so refer to test_load for that.
    with pytest.raises(Exception):
        container_registry.addContainerType(None)

##  Tests the creation of the container registry.
#
#   This is tested using the fixture to create a container registry.
#
#   \param container_registry A newly created container registry instance, from
#   a fixture.
def test_create(container_registry):
    assert container_registry != None

##  Individual test cases for test_findDefinitionContainers as well as
#   test_findInstanceContainers.
#
#   Each entry has a descriptive name for debugging.
#   Each entry also has a list of "containers", each of which is an arbitrary
#   dictionary of metadata. Each dictionary MUST have an ID entry, which will be
#   taken out and used as ID for the container.
#   Each entry also has a "filter" dictionary, which is used as filter for the
#   metadata of the containers.
#   Each entry also has an "answer", which is a list of dictionaries again
#   representing the containers that must be returned by the search.
test_findContainers_data = [
    {
        "name": "No containers",
        "containers": [

        ],
        "filter": { "id": "a" },
        "result": [
            # Empty.
        ]
    },
    {
        "name": "Single ID match",
        "containers": [
            { "id": "a" },
            { "id": "b" }
        ],
        "filter": { "id": "a" },
        "result": [
            { "id": "a" }
        ]
    },
    {
        "name": "Double ID match",
        "containers": [
            { "id": "a" },
            { "id": "b" },
            { "id": "a" }
        ],
        "filter": { "id": "a" },
        "result": [
            { "id": "a" },
            { "id": "a" }
        ]
    },
    {
        "name": "Multiple constraints",
        "containers": [
            { "id": "a", "number": 1, "bool": False },
            { "id": "b", "number": 2, "bool": False },
            { "id": "c", "number": 2, "bool": True },
            { "id": "d", "number": 2, "bool": False }
        ],
        "filter": { "number": 2, "bool": False },
        "result": [
            { "id": "b", "number": 2, "bool": False },
            { "id": "d", "number": 2, "bool": False }
        ]
    },
    {
        "name": "Mixed Type",
        "containers": [
            { "id": "a", "number": 1, "mixed": "z" },
            { "id": "b", "number": 1, "mixed": 9 },
            { "id": "c", "number": 2, "mixed": 9 }
        ],
        "filter": { "number": 1, "mixed": 9 },
        "result": [
            { "id": "b", "number": 1, "mixed": 9 }
        ]
    }
]

##  Tests the findDefinitionContainers function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findDefinitionContainers(container_registry, data):
    for container in data["containers"]: # Fill the registry with mock containers.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        definition_container = UM.Settings.DefinitionContainer(container_id, container)
        container_registry.addContainer(definition_container)

    results = container_registry.findDefinitionContainers(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the findInstanceContainers function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findInstanceContainers(container_registry, data):
    for container in data["containers"]: # Fill the registry with mock containers.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        instance_container = UM.Settings.InstanceContainer(container_id, container)
        container_registry.addContainer(instance_container)

    results = container_registry.findInstanceContainers(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the findContainerStacks function.
#
#   \param container_registry A new container registry from a fixture.
#   \param data The data for the tests. Loaded from test_findContainers_data.
@pytest.mark.parametrize("data", test_findContainers_data)
def test_findContainerStacks(container_registry, data):
    for container in data["containers"]: # Fill the registry with container stacks.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        container_stack = UM.Settings.ContainerStack(container_id, container)
        container_registry.addContainer(container_stack)

    results = container_registry.findContainerStacks(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])

##  Tests the loading of containers into the registry.
#
#   \param container_registry A new container registry from a fixture.
def test_load(container_registry):
    container_registry.load()

    definitions = container_registry.findDefinitionContainers(id = "single_setting")
    assert len(definitions) == 1

    definition = definitions[0]
    assert definition.getId() == "single_setting"

    definitions = container_registry.findDefinitionContainers(author = "Ultimaker")
    assert len(definitions) == 3

    ids_found = []
    for definition in definitions:
        ids_found.append(definition.getId())

    assert "metadata" in ids_found
    assert "single_setting" in ids_found
    assert "inherits" in ids_found

##  Helper function to verify if the metadata of the answers matches required
#   metadata.
#
#   This basically compares two sets. They are provided as two lists, but the
#   order doesn't matter.
#
#   \param answer A list of containers, each of which has metadata.
#   \param ground_truth A list of dictionaries, describing the metadata of each
#   required container.
def _verifyMetaDataMatches(answer, ground_truth):
    assert len(answer) == len(ground_truth)

    matches = 0
    for result in answer: # Go through all results and match them with our expected data.
        for required in list(ground_truth): # Iterate over a copy of the list so we do not modify the original data.
            if "id" in required: # Special casing for ID since that is not in the metadata.
                if result.getId() != required["id"]:
                    continue # No match.
                del required["id"] # Remove ID from the expected metadata since it is not part of the metadata.

            if result.getMetaData() == required:
                # If the metadata matches, we know this entry is valid.
                # Note that this requires specifying all metadata in the expected results.
                matches += 1
                break # We have a valid match.
    assert matches == len(ground_truth)<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
The Salt Cloud Runner
=====================

This runner wraps the functionality of salt cloud making salt cloud routines
available to all internal apis via the runner system
'''

# Import python libs
import os

# Import Salt libs
import salt.cloud

def _get_client():
    '''
    Return a cloud client
    '''
    client = salt.cloud.CloudClient(
            os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud')
            )
    return client

def list_sizes(provider='all'):
    '''
    List cloud provider sizes for the given providers
    '''
    client = _get_client()
    sizes = client.list_sizes(provider)
    return sizes

def list_images(provider='all'):
    '''
    List cloud provider images for the given providers
    '''
    client = _get_client()
    images = client.list_images(provider)
    return images

def list_locations(provider='all'):
    '''
    List cloud provider sizes for the given providers
    '''
    client = _get_client()
    locations = client.list_locations(provider)
    return locations

def query(query_type='list_nodes'):
    '''
    List cloud provider data for all providers
    '''
    client = _get_client()
    info = client.query(query_type)
    return info

def full_query(query_type='list_nodes_full'):
    '''
    List all available cloud provider data
    '''
    client = _get_client()
    info = client.full_query(query_type)
    return info

def select_query(query_type='list_nodes_select'):
    '''
    List selected nodes
    '''
    client = _get_client()
    info = client.select_query(query_type)
    return info

def profile(prof, names, **kwargs):
    '''
    Create a cloud vm with the given profile and names, names can be a list
    or comma delimited string
    '''
    client = _get_client()
    info = client.profile(prof, names, **kwargs)
    return info

def map_run(path, **kwargs):
    '''
    Execute a salt cloud map file
    '''
    client = _get_client()
    info = client.map_run(path, **kwargs)
    return info

def destroy(names):
    '''
    Destroy the named vm(s)
    '''
    client = _get_client()
    info = client.destroy(names)
    return info

def action(
        fun=None,
        cloudmap=None,
        names=None,
        provider=None,
        instance=None,
        **kwargs):
    '''
    Execute a single action on the given map/provider/instance
    '''
    client = _get_client()
    info = client.action(fun, cloudmap, names, provider, instance, kwargs)
    return info

def create(provider, instances, **kwargs):
    '''
    Create an instance using Salt Cloud

    CLI Example:

    .. code-block:: bash

        salt-run cloud.create my-ec2-config myinstance \
            image=ami-1624987f size='Micro Instance' ssh_username=ec2-user \
            securitygroup=default delvol_on_destroy=True
    '''
    create_kwargs = {}
    for kwarg in kwargs:
        if not kwarg.startswith('__'):
            create_kwargs[kwarg] = kwargs[kwarg]
    client = _get_client()
    info = client.create(provider, instances, **create_kwargs)
    return info
<commit_msg>Changes names to instances<commit_after># -*- coding: utf-8 -*-
'''
The Salt Cloud Runner
=====================

This runner wraps the functionality of salt cloud making salt cloud routines
available to all internal apis via the runner system
'''

# Import python libs
import os

# Import Salt libs
import salt.cloud

def _get_client():
    '''
    Return a cloud client
    '''
    client = salt.cloud.CloudClient(
            os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud')
            )
    return client

def list_sizes(provider='all'):
    '''
    List cloud provider sizes for the given providers
    '''
    client = _get_client()
    sizes = client.list_sizes(provider)
    return sizes

def list_images(provider='all'):
    '''
    List cloud provider images for the given providers
    '''
    client = _get_client()
    images = client.list_images(provider)
    return images

def list_locations(provider='all'):
    '''
    List cloud provider sizes for the given providers
    '''
    client = _get_client()
    locations = client.list_locations(provider)
    return locations

def query(query_type='list_nodes'):
    '''
    List cloud provider data for all providers
    '''
    client = _get_client()
    info = client.query(query_type)
    return info

def full_query(query_type='list_nodes_full'):
    '''
    List all available cloud provider data
    '''
    client = _get_client()
    info = client.full_query(query_type)
    return info

def select_query(query_type='list_nodes_select'):
    '''
    List selected nodes
    '''
    client = _get_client()
    info = client.select_query(query_type)
    return info

def profile(prof, instances, **kwargs):
    '''
    Create a cloud vm with the given profile and instances, instances can be a list
    or comma delimited string
    '''
    client = _get_client()
    info = client.profile(prof, instances, **kwargs)
    return info

def map_run(path, **kwargs):
    '''
    Execute a salt cloud map file
    '''
    client = _get_client()
    info = client.map_run(path, **kwargs)
    return info

def destroy(instances):
    '''
    Destroy the named vm(s)
    '''
    client = _get_client()
    info = client.destroy(instances)
    return info

def action(
        fun=None,
        cloudmap=None,
        instances=None,
        provider=None,
        instance=None,
        **kwargs):
    '''
    Execute a single action on the given map/provider/instance
    '''
    client = _get_client()
    info = client.action(fun, cloudmap, instances, provider, instance, kwargs)
    return info

def create(provider, instances, **kwargs):
    '''
    Create an instance using Salt Cloud

    CLI Example:

    .. code-block:: bash

        salt-run cloud.create my-ec2-config myinstance \
            image=ami-1624987f size='Micro Instance' ssh_username=ec2-user \
            securitygroup=default delvol_on_destroy=True
    '''
    create_kwargs = {}
    for kwarg in kwargs:
        if not kwarg.startswith('__'):
            create_kwargs[kwarg] = kwargs[kwarg]
    client = _get_client()
    info = client.create(provider, instances, **create_kwargs)
    return info
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
r'''
Execution of Salt modules from within states
============================================

These states allow individual execution module calls to be made via states. To
call a single module function use a :mod:`module.run <salt.states.module.run>`
state:

.. code-block:: yaml

    mine.send:
      module.run:
        - name: network.interfaces

Note that this example is probably unnecessary to use in practice, since the
``mine_functions`` and ``mine_interval`` config parameters can be used to
schedule updates for the mine (see :ref:`here <salt-mine>` for more
info).

It is sometimes desirable to trigger a function call after a state is executed,
for this the :mod:`module.wait <salt.states.module.wait>` state can be used:

.. code-block:: yaml

    mine.send:
      module.wait:
        - name: network.interfaces
        - watch:
          - file: /etc/network/interfaces

All arguments that the ``module`` state does not consume are passed through to
the execution module function being executed:

.. code-block:: yaml

    fetch_out_of_band:
      module.run:
        - name: git.fetch
        - cwd: /path/to/my/repo
        - user: myuser
        - opts: '--all'

Due to how the state system works, if a module function accepts an
argument called, ``name``, then ``m_name`` must be used to specify that
argument, to avoid a collision with the ``name`` argument.

Here is a list of keywords hidden by the state system, which must be prefixed
with ``m_``:

* fun
* name
* names
* state
* saltenv

For example:

.. code-block:: yaml

    disable_nfs:
      module.run:
        - name: service.disable
        - m_name: nfs

Note that some modules read all or some of the arguments from a list of keyword
arguments. For example:

.. code-block:: yaml

    mine.send:
      module.run:
        - func: network.ip_addrs
        - kwargs:
            interface: eth0

.. code-block:: yaml

    cloud.create:
      module.run:
        - func: cloud.create
        - provider: test-provider
        - m_names:
          - test-vlad
        - kwargs: {
              ssh_username: 'ubuntu',
              image: 'ami-8d6d9daa',
              securitygroup: 'default',
              size: 'c3.large',
              location: 'ap-northeast-1',
              delvol_on_destroy: 'True'
          }

Another example that creates a recurring task that runs a batch file on a
Windows system:

.. code-block:: yaml

    eventsviewer:
      module.run:
        - name: task.create_task
        - m_name: 'events-viewer'
        - user_name: System
        - kwargs: {
              action_type: 'Execute',
              cmd: 'c:\netops\scripts\events_viewer.bat',
              trigger_type: 'Daily',
              start_date: '2017-1-20',
              start_time: '11:59PM'
        }
'''
from __future__ import absolute_import

# Import salt libs
import salt.loader
import salt.utils
import salt.utils.jid
from salt.ext.six.moves import range
from salt.exceptions import SaltInvocationError

def wait(name, **kwargs):
    '''
    Run a single module function only if the watch statement calls it

    ``name``
        The module function to execute

    ``**kwargs``
        Pass any arguments needed to execute the function

    .. note::
        Like the :mod:`cmd.run <salt.states.cmd.run>` state, this state will
        return ``True`` but not actually execute, unless one of the following
        two things happens:

        1. The state has a :ref:`watch requisite <requisites-watch>`, and
           the state which it is watching changes.

        2. Another state has a :ref:`watch_in requisite
           <requisites-watch-in>` which references this state, and the state
           wth the ``watch_in`` changes.
    '''
    return {'name': name,
            'changes': {},
            'result': True,
            'comment': ''}

# Alias module.watch to module.wait
watch = salt.utils.alias_function(wait, 'watch')

def xrun(**kwargs):
    '''
    Run a single module function or a range of module functions in a batch.
    Supersedes `module.run` function, which requires `m_` prefix to function-specific parameters.

    :param returner:
        Specify a common returner for the whole batch to send the return data

    :param kwargs:
        Pass any arguments needed to execute the function(s)

    .. code-block:: yaml
      some_id_of_state:
        module.xrun:
          - network.ip_addrs:
            - interface: eth0
          - cloud.create:
            - names:
              - test-isbm-1
              - test-isbm-2
            - ssh_username: sles
            - image: sles12sp2
            - securitygroup: default
            - size: 'c3.large'
            - location: ap-northeast-1
            - delvol_on_destroy: True

    :return:
    '''

    'name' in kwargs and kwargs.pop('name')
    ret = {
        'name': kwargs.keys(),
        'changes': {},
        'comment': '',
        'result': None,
    }

    functions = [func for func in kwargs.keys() if '.' in func]
    for func in functions:
        if func not in __salt__:
            ret['comment'] = "Module function '{0}' is not available".format(func)
            ret['result'] = False
        elif __opts__['test']:
            ret['comment'] = "Module function '{0}' is set to execute".format(func)
            ret['result'] = True

    if ret['result'] is None:
        ret['result'] = True

        failures = []
        success = []
        for func in functions:
            try:
                func_ret = _call_function(func, returner=kwargs.get('returner'),
                                          func_args=kwargs.get(func))
                if not _get_result(func_ret, ret['changes'].get('ret', {})):
                    failures.append("'{0}' failed: {1}".format(func_ret.get('comment', '(error message N/A)')))
                else:
                    success.append('{0}: {1}'.format(
                        func, func_ret.get('comment', 'Success') if isinstance(func_ret, dict) else func_ret))
            except (SaltInvocationError, TypeError) as ex:
                failures.append("'{0}' failed: {1}".format(func, ex))
        ret['comment'] = ', '.join(failures + success)
        ret['result'] = not bool(failures)

    return ret

def _call_function(name, returner=None, **kwargs):
    '''
    Calls a function from the specified module.

    :param name:
    :param kwargs:
    :return:
    '''
    argspec = salt.utils.args.get_function_argspec(__salt__[name])
    func_kw = dict(zip(argspec.args[-len(argspec.defaults or []):], argspec.defaults or []))
    func_args = []
    for funcset in kwargs.get('func_args') or {}:
        if isinstance(funcset, dict):
            func_kw.update(funcset)
        else:
            func_args.append(funcset)

    missing = []
    for arg in argspec.args:
        if arg not in func_kw:
            missing.append(arg)
    if missing:
        raise SaltInvocationError('Missing arguments: {0}'.format(', '.join(missing)))

    mret = __salt__[name](*func_args, **func_kw)
    if returner is not None:
        returners = salt.loader.returners(__opts__, __salt__)
        if returner in returners:
            returners[returner]({'id': __opts__['id'], 'ret': mret,
                                 'fun': name, 'jid': salt.utils.jid.gen_jid()})

    return mret

def run(name, **kwargs):
    '''
    Run a single module function

    ``name``
        The module function to execute

    ``returner``
        Specify the returner to send the return of the module execution to

    ``kwargs``
        Pass any arguments needed to execute the function
    '''
    ret = {'name': name,
           'changes': {},
           'comment': '',
           'result': None}
    if name not in __salt__:
        ret['comment'] = 'Module function {0} is not available'.format(name)
        ret['result'] = False
        return ret

    if __opts__['test']:
        ret['comment'] = 'Module function {0} is set to execute'.format(name)
        return ret

    aspec = salt.utils.args.get_function_argspec(__salt__[name])
    args = []
    defaults = {}

    arglen = 0
    deflen = 0
    if isinstance(aspec.args, list):
        arglen = len(aspec.args)
    if isinstance(aspec.defaults, tuple):
        deflen = len(aspec.defaults)
    # Match up the defaults with the respective args
    for ind in range(arglen - 1, -1, -1):
        minus = arglen - ind
        if deflen - minus > -1:
            defaults[aspec.args[ind]] = aspec.defaults[-minus]
    # overwrite passed default kwargs
    for arg in defaults:
        if arg == 'name':
            if 'm_name' in kwargs:
                defaults[arg] = kwargs.pop('m_name')
        elif arg == 'fun':
            if 'm_fun' in kwargs:
                defaults[arg] = kwargs.pop('m_fun')
        elif arg == 'state':
            if 'm_state' in kwargs:
                defaults[arg] = kwargs.pop('m_state')
        elif arg == 'saltenv':
            if 'm_saltenv' in kwargs:
                defaults[arg] = kwargs.pop('m_saltenv')
        if arg in kwargs:
            defaults[arg] = kwargs.pop(arg)
    missing = set()
    for arg in aspec.args:
        if arg == 'name':
            rarg = 'm_name'
        elif arg == 'fun':
            rarg = 'm_fun'
        elif arg == 'names':
            rarg = 'm_names'
        elif arg == 'state':
            rarg = 'm_state'
        elif arg == 'saltenv':
            rarg = 'm_saltenv'
        else:
            rarg = arg
        if rarg not in kwargs and arg not in defaults:
            missing.add(rarg)
            continue
        if arg in defaults:
            args.append(defaults[arg])
        else:
            args.append(kwargs.pop(rarg))
    if missing:
        comment = 'The following arguments are missing:'
        for arg in missing:
            comment += ' {0}'.format(arg)
        ret['comment'] = comment
        ret['result'] = False
        return ret

    if aspec.varargs and aspec.varargs in kwargs:
        varargs = kwargs.pop(aspec.varargs)

        if not isinstance(varargs, list):
            msg = "'{0}' must be a list."
            ret['comment'] = msg.format(aspec.varargs)
            ret['result'] = False
            return ret

        args.extend(varargs)

    nkwargs = {}
    if aspec.keywords and aspec.keywords in kwargs:
        nkwargs = kwargs.pop(aspec.keywords)

        if not isinstance(nkwargs, dict):
            msg = "'{0}' must be a dict."
            ret['comment'] = msg.format(aspec.keywords)
            ret['result'] = False
            return ret

    try:
        if aspec.keywords:
            mret = __salt__[name](*args, **nkwargs)
        else:
            mret = __salt__[name](*args)
    except Exception as e:
        ret['comment'] = 'Module function {0} threw an exception. Exception: {1}'.format(name, e)
        ret['result'] = False
        return ret
    else:
        if mret is not None or mret is not {}:
            ret['changes']['ret'] = mret

    if 'returner' in kwargs:
        ret_ret = {
                'id': __opts__['id'],
                'ret': mret,
                'fun': name,
                'jid': salt.utils.jid.gen_jid()}
        returners = salt.loader.returners(__opts__, __salt__)
        if kwargs['returner'] in returners:
            returners[kwargs['returner']](ret_ret)
    ret['comment'] = 'Module function {0} executed'.format(name)
    ret['result'] = _get_result(mret, ret['changes'].get('ret', {}))

    return ret

def _get_result(func_ret, changes):
    res = True
    # if mret is a dict and there is retcode and its non-zero
    if isinstance(func_ret, dict) and func_ret.get('retcode', 0) != 0:
        res = False
        # if its a boolean, return that as the result
    elif isinstance(func_ret, bool):
        res = func_ret
    else:
        changes_ret = changes.get('ret', {})
        if isinstance(changes_ret, dict):
            if isinstance(changes_ret.get('result', {}), bool):
                res = changes_ret.get('result', {})
            elif changes_ret.get('retcode', 0) != 0:
                res = False

    return res

mod_watch = salt.utils.alias_function(run, 'mod_watch')
<commit_msg>Properly handle return of False<commit_after># -*- coding: utf-8 -*-
r'''
Execution of Salt modules from within states
============================================

These states allow individual execution module calls to be made via states. To
call a single module function use a :mod:`module.run <salt.states.module.run>`
state:

.. code-block:: yaml

    mine.send:
      module.run:
        - name: network.interfaces

Note that this example is probably unnecessary to use in practice, since the
``mine_functions`` and ``mine_interval`` config parameters can be used to
schedule updates for the mine (see :ref:`here <salt-mine>` for more
info).

It is sometimes desirable to trigger a function call after a state is executed,
for this the :mod:`module.wait <salt.states.module.wait>` state can be used:

.. code-block:: yaml

    mine.send:
      module.wait:
        - name: network.interfaces
        - watch:
          - file: /etc/network/interfaces

All arguments that the ``module`` state does not consume are passed through to
the execution module function being executed:

.. code-block:: yaml

    fetch_out_of_band:
      module.run:
        - name: git.fetch
        - cwd: /path/to/my/repo
        - user: myuser
        - opts: '--all'

Due to how the state system works, if a module function accepts an
argument called, ``name``, then ``m_name`` must be used to specify that
argument, to avoid a collision with the ``name`` argument.

Here is a list of keywords hidden by the state system, which must be prefixed
with ``m_``:

* fun
* name
* names
* state
* saltenv

For example:

.. code-block:: yaml

    disable_nfs:
      module.run:
        - name: service.disable
        - m_name: nfs

Note that some modules read all or some of the arguments from a list of keyword
arguments. For example:

.. code-block:: yaml

    mine.send:
      module.run:
        - func: network.ip_addrs
        - kwargs:
            interface: eth0

.. code-block:: yaml

    cloud.create:
      module.run:
        - func: cloud.create
        - provider: test-provider
        - m_names:
          - test-vlad
        - kwargs: {
              ssh_username: 'ubuntu',
              image: 'ami-8d6d9daa',
              securitygroup: 'default',
              size: 'c3.large',
              location: 'ap-northeast-1',
              delvol_on_destroy: 'True'
          }

Another example that creates a recurring task that runs a batch file on a
Windows system:

.. code-block:: yaml

    eventsviewer:
      module.run:
        - name: task.create_task
        - m_name: 'events-viewer'
        - user_name: System
        - kwargs: {
              action_type: 'Execute',
              cmd: 'c:\netops\scripts\events_viewer.bat',
              trigger_type: 'Daily',
              start_date: '2017-1-20',
              start_time: '11:59PM'
        }
'''
from __future__ import absolute_import

# Import salt libs
import salt.loader
import salt.utils
import salt.utils.jid
from salt.ext.six.moves import range
from salt.exceptions import SaltInvocationError

def wait(name, **kwargs):
    '''
    Run a single module function only if the watch statement calls it

    ``name``
        The module function to execute

    ``**kwargs``
        Pass any arguments needed to execute the function

    .. note::
        Like the :mod:`cmd.run <salt.states.cmd.run>` state, this state will
        return ``True`` but not actually execute, unless one of the following
        two things happens:

        1. The state has a :ref:`watch requisite <requisites-watch>`, and
           the state which it is watching changes.

        2. Another state has a :ref:`watch_in requisite
           <requisites-watch-in>` which references this state, and the state
           wth the ``watch_in`` changes.
    '''
    return {'name': name,
            'changes': {},
            'result': True,
            'comment': ''}

# Alias module.watch to module.wait
watch = salt.utils.alias_function(wait, 'watch')

def xrun(**kwargs):
    '''
    Run a single module function or a range of module functions in a batch.
    Supersedes `module.run` function, which requires `m_` prefix to function-specific parameters.

    :param returner:
        Specify a common returner for the whole batch to send the return data

    :param kwargs:
        Pass any arguments needed to execute the function(s)

    .. code-block:: yaml
      some_id_of_state:
        module.xrun:
          - network.ip_addrs:
            - interface: eth0
          - cloud.create:
            - names:
              - test-isbm-1
              - test-isbm-2
            - ssh_username: sles
            - image: sles12sp2
            - securitygroup: default
            - size: 'c3.large'
            - location: ap-northeast-1
            - delvol_on_destroy: True

    :return:
    '''

    'name' in kwargs and kwargs.pop('name')
    ret = {
        'name': kwargs.keys(),
        'changes': {},
        'comment': '',
        'result': None,
    }

    functions = [func for func in kwargs.keys() if '.' in func]
    for func in functions:
        if func not in __salt__:
            ret['comment'] = "Module function '{0}' is not available".format(func)
            ret['result'] = False
        elif __opts__['test']:
            ret['comment'] = "Module function '{0}' is set to execute".format(func)
            ret['result'] = True

    if ret['result'] is None:
        ret['result'] = True

        failures = []
        success = []
        for func in functions:
            try:
                func_ret = _call_function(func, returner=kwargs.get('returner'),
                                          func_args=kwargs.get(func))
                if not _get_result(func_ret, ret['changes'].get('ret', {})):
                    if isinstance(func_ret, dict):
                        failures.append("'{0}' failed: {1}".format(func_ret.get('comment', '(error message N/A)')))
                else:
                    success.append('{0}: {1}'.format(
                        func, func_ret.get('comment', 'Success') if isinstance(func_ret, dict) else func_ret))
            except (SaltInvocationError, TypeError) as ex:
                failures.append("'{0}' failed: {1}".format(func, ex))
        ret['comment'] = ', '.join(failures + success)
        ret['result'] = not bool(failures)

    return ret

def _call_function(name, returner=None, **kwargs):
    '''
    Calls a function from the specified module.

    :param name:
    :param kwargs:
    :return:
    '''
    argspec = salt.utils.args.get_function_argspec(__salt__[name])
    func_kw = dict(zip(argspec.args[-len(argspec.defaults or []):], argspec.defaults or []))
    func_args = []
    for funcset in kwargs.get('func_args') or {}:
        if isinstance(funcset, dict):
            func_kw.update(funcset)
        else:
            func_args.append(funcset)

    missing = []
    for arg in argspec.args:
        if arg not in func_kw:
            missing.append(arg)
    if missing:
        raise SaltInvocationError('Missing arguments: {0}'.format(', '.join(missing)))

    mret = __salt__[name](*func_args, **func_kw)
    if returner is not None:
        returners = salt.loader.returners(__opts__, __salt__)
        if returner in returners:
            returners[returner]({'id': __opts__['id'], 'ret': mret,
                                 'fun': name, 'jid': salt.utils.jid.gen_jid()})

    return mret

def run(name, **kwargs):
    '''
    Run a single module function

    ``name``
        The module function to execute

    ``returner``
        Specify the returner to send the return of the module execution to

    ``kwargs``
        Pass any arguments needed to execute the function
    '''
    ret = {'name': name,
           'changes': {},
           'comment': '',
           'result': None}
    if name not in __salt__:
        ret['comment'] = 'Module function {0} is not available'.format(name)
        ret['result'] = False
        return ret

    if __opts__['test']:
        ret['comment'] = 'Module function {0} is set to execute'.format(name)
        return ret

    aspec = salt.utils.args.get_function_argspec(__salt__[name])
    args = []
    defaults = {}

    arglen = 0
    deflen = 0
    if isinstance(aspec.args, list):
        arglen = len(aspec.args)
    if isinstance(aspec.defaults, tuple):
        deflen = len(aspec.defaults)
    # Match up the defaults with the respective args
    for ind in range(arglen - 1, -1, -1):
        minus = arglen - ind
        if deflen - minus > -1:
            defaults[aspec.args[ind]] = aspec.defaults[-minus]
    # overwrite passed default kwargs
    for arg in defaults:
        if arg == 'name':
            if 'm_name' in kwargs:
                defaults[arg] = kwargs.pop('m_name')
        elif arg == 'fun':
            if 'm_fun' in kwargs:
                defaults[arg] = kwargs.pop('m_fun')
        elif arg == 'state':
            if 'm_state' in kwargs:
                defaults[arg] = kwargs.pop('m_state')
        elif arg == 'saltenv':
            if 'm_saltenv' in kwargs:
                defaults[arg] = kwargs.pop('m_saltenv')
        if arg in kwargs:
            defaults[arg] = kwargs.pop(arg)
    missing = set()
    for arg in aspec.args:
        if arg == 'name':
            rarg = 'm_name'
        elif arg == 'fun':
            rarg = 'm_fun'
        elif arg == 'names':
            rarg = 'm_names'
        elif arg == 'state':
            rarg = 'm_state'
        elif arg == 'saltenv':
            rarg = 'm_saltenv'
        else:
            rarg = arg
        if rarg not in kwargs and arg not in defaults:
            missing.add(rarg)
            continue
        if arg in defaults:
            args.append(defaults[arg])
        else:
            args.append(kwargs.pop(rarg))
    if missing:
        comment = 'The following arguments are missing:'
        for arg in missing:
            comment += ' {0}'.format(arg)
        ret['comment'] = comment
        ret['result'] = False
        return ret

    if aspec.varargs and aspec.varargs in kwargs:
        varargs = kwargs.pop(aspec.varargs)

        if not isinstance(varargs, list):
            msg = "'{0}' must be a list."
            ret['comment'] = msg.format(aspec.varargs)
            ret['result'] = False
            return ret

        args.extend(varargs)

    nkwargs = {}
    if aspec.keywords and aspec.keywords in kwargs:
        nkwargs = kwargs.pop(aspec.keywords)

        if not isinstance(nkwargs, dict):
            msg = "'{0}' must be a dict."
            ret['comment'] = msg.format(aspec.keywords)
            ret['result'] = False
            return ret

    try:
        if aspec.keywords:
            mret = __salt__[name](*args, **nkwargs)
        else:
            mret = __salt__[name](*args)
    except Exception as e:
        ret['comment'] = 'Module function {0} threw an exception. Exception: {1}'.format(name, e)
        ret['result'] = False
        return ret
    else:
        if mret is not None or mret is not {}:
            ret['changes']['ret'] = mret

    if 'returner' in kwargs:
        ret_ret = {
                'id': __opts__['id'],
                'ret': mret,
                'fun': name,
                'jid': salt.utils.jid.gen_jid()}
        returners = salt.loader.returners(__opts__, __salt__)
        if kwargs['returner'] in returners:
            returners[kwargs['returner']](ret_ret)
    ret['comment'] = 'Module function {0} executed'.format(name)
    ret['result'] = _get_result(mret, ret['changes'].get('ret', {}))

    return ret

def _get_result(func_ret, changes):
    res = True
    # if mret is a dict and there is retcode and its non-zero
    if isinstance(func_ret, dict) and func_ret.get('retcode', 0) != 0:
        res = False
        # if its a boolean, return that as the result
    elif isinstance(func_ret, bool):
        res = func_ret
    else:
        changes_ret = changes.get('ret', {})
        if isinstance(changes_ret, dict):
            if isinstance(changes_ret.get('result', {}), bool):
                res = changes_ret.get('result', {})
            elif changes_ret.get('retcode', 0) != 0:
                res = False

    return res

mod_watch = salt.utils.alias_function(run, 'mod_watch')
<|endoftext|>