コード例 #1
0
ファイル: query.py プロジェクト: DaveQB/salt
class Query(object):
    '''
    Query the system.
    This class is actually puts all Salt features together,
    so there would be no need to pick it from various places.
    '''

    # Configuration: config files
    # Identity: users/groups
    # Software: packages, patterns, repositories
    # Services
    # System: distro, RAM etc
    # Changes: all files that are managed and were changed from the original
    # all: include all scopes (scary!)
    # payload: files that are not managed

    SCOPES = ["changes", "configuration", "identity", "system", "software", "services", "payload", "all"]

    def __init__(self, scope):
        '''
        Constructor.

        :param scope:
        :return:
        '''
        if scope not in self.SCOPES:
            raise InspectorQueryException(
                "Unknown scope: {0}. Must be one of: {1}".format(repr(scope), ", ".join(self.SCOPES)))
        self.scope = '_' + scope
        self.db = DBHandle(globals()['__salt__']['config.get']('inspector.db', ''))
        self.local_identity = dict()

    def __call__(self, *args, **kwargs):
        '''
        Call the query with the defined scope.

        :param args:
        :param kwargs:
        :return:
        '''

        return getattr(self, self.scope)(*args, **kwargs)

    def _changes(self, *args, **kwargs):
        '''
        Returns all diffs to the configuration files.
        '''
        raise Exception("Not yet implemented")

    def _configuration(self, *args, **kwargs):
        '''
        Return configuration files.
        '''

        data = dict()
        self.db.open()
        self.db.cursor.execute("SELECT id, name FROM inspector_pkg")
        for pkg_id, pkg_name in self.db.cursor.fetchall():
            self.db.cursor.execute("SELECT id, path FROM inspector_pkg_cfg_files WHERE pkgid=?", (pkg_id,))
            configs = list()
            for cnf_id, cnf_name in self.db.cursor.fetchall():
                configs.append(cnf_name)
            data[pkg_name] = configs
        self.db.close()

        if not data:
            raise InspectorQueryException("No inspected configuration yet available.")

        return data

    def _get_local_users(self, disabled=None):
        '''
        Return all known local accounts to the system.
        '''
        users = dict()
        path = '/etc/passwd'
        with salt.utils.fopen(path, 'r') as fp_:
            for line in fp_:
                line = line.strip()
                if ':' not in line:
                    continue
                name, password, uid, gid, gecos, directory, shell = line.split(':')
                active = not (password == '*' or password.startswith('!'))
                if (disabled is False and active) or (disabled is True and not active) or disabled is None:
                    users[name] = {
                        'uid': uid,
                        'git': gid,
                        'info': gecos,
                        'home': directory,
                        'shell': shell,
                        'disabled': not active
                    }

        return users

    def _get_local_groups(self):
        '''
        Return all known local groups to the system.
        '''
        groups = dict()
        path = '/etc/group'
        with salt.utils.fopen(path, 'r') as fp_:
            for line in fp_:
                line = line.strip()
                if ':' not in line:
                    continue
                name, password, gid, users = line.split(':')
                groups[name] = {
                    'gid': gid,
                }

                if users:
                    groups[name]['users'] = users.split(',')

        return groups

    def _get_external_accounts(self, locals):
        '''
        Return all known accounts, excluding local accounts.
        '''
        users = dict()
        out = __salt__['cmd.run_all']("passwd -S -a")
        if out['retcode']:
            # System does not supports all accounts descriptions, just skipping.
            return users
        status = {'L': 'Locked', 'NP': 'No password', 'P': 'Usable password', 'LK': 'Locked'}
        for data in [elm.strip().split(" ") for elm in out['stdout'].split(os.linesep) if elm.strip()]:
            if len(data) < 2:
                continue
            name, login = data[:2]
            if name not in locals:
                users[name] = {
                    'login': login,
                    'status': status.get(login, 'N/A')
                }

        return users

    def _identity(self, *args, **kwargs):
        '''
        Local users and groups.

        accounts
            Can be either 'local', 'remote' or 'all' (equal to "local,remote").
            Remote accounts cannot be resolved on all systems, but only
            those, which supports 'passwd -S -a'.

        disabled
            True (or False, default) to return only disabled accounts.
        '''
        LOCAL = 'local accounts'
        EXT = 'external accounts'

        data = dict()
        data[LOCAL] = self._get_local_users(disabled=kwargs.get('disabled'))
        data[EXT] = self._get_external_accounts(data[LOCAL].keys()) or 'N/A'
        data['local groups'] = self._get_local_groups()

        return data

    def _system(self, *args, **kwargs):
        '''
        This basically calls grains items and picks out only
        necessary information in a certain structure.

        :param args:
        :param kwargs:
        :return:
        '''
        sysinfo = SysInfo(__grains__.get("kernel"))

        data = dict()
        data['cpu'] = sysinfo._get_cpu()
        data['disks'] = sysinfo._get_fs()
        data['mounts'] = sysinfo._get_mounts()
        data['memory'] = sysinfo._get_mem()
        data['network'] = sysinfo._get_network()
        data['os'] = sysinfo._get_os()

        return data

    def _software(self, *args, **kwargs):
        '''
        Return installed software.
        '''
        data = dict()
        if 'exclude' in kwargs:
            excludes = kwargs['exclude'].split(",")
        else:
            excludes = list()

        os_family = __grains__.get("os_family").lower()

        # Get locks
        if os_family == 'suse':
            LOCKS = "pkg.list_locks"
            if 'products' not in excludes:
                products = __salt__['pkg.list_products']()
                if products:
                    data['products'] = products
        elif os_family == 'redhat':
            LOCKS = "pkg.get_locked_packages"
        else:
            LOCKS = None

        if LOCKS and 'locks' not in excludes:
            locks = __salt__[LOCKS]()
            if locks:
                data['locks'] = locks

        # Get patterns
        if os_family == 'suse':
            PATTERNS = 'pkg.list_installed_patterns'
        elif os_family == 'redhat':
            PATTERNS = 'pkg.group_list'
        else:
            PATTERNS = None

        if PATTERNS and 'patterns' not in excludes:
            patterns = __salt__[PATTERNS]()
            if patterns:
                data['patterns'] = patterns

        # Get packages
        if 'packages' not in excludes:
            data['packages'] = __salt__['pkg.list_pkgs']()

        # Get repositories
        if 'repositories' not in excludes:
            repos = __salt__['pkg.list_repos']()
            if repos:
                data['repositories'] = repos

        return data

    def _services(self, *args, **kwargs):
        '''
        Get list of enabled and disabled services on the particular system.
        '''
        return {
            'enabled': __salt__['service.get_enabled'](),
            'disabled': __salt__['service.get_disabled'](),
        }

    def _id_resolv(self, iid, named=True, uid=True):
        '''
        Resolve local users and groups.

        :param iid:
        :param named:
        :param uid:
        :return:
        '''

        if not self.local_identity:
            self.local_identity['users'] = self._get_local_users()
            self.local_identity['groups'] = self._get_local_groups()

        if not named:
            return iid

        for name, meta in self.local_identity[uid and 'users' or 'groups'].items():
            if (uid and int(meta.get('uid', -1)) == iid) or (not uid and int(meta.get('gid', -1)) == iid):
                return name

        return iid

    def _payload(self, *args, **kwargs):
        '''
        Find all unmanaged files.

        Parameters:

        * **filter**: Include only results which path starts from the filter string.
        * **time**: Display time in Unix ticks or format according to the configured TZ (default)
                    Values: ticks, tz (default)
        * **size**: Format size. Values: B, KB, MB, GB
        * **owners**: Resolve UID/GID to an actual names or leave them numeric (default).
                      Values: name (default), id
        * **type**: Comma-separated type of included payload: dir (or directory), link and/or file.
        * **brief**: Return just a list of matches, if True. Default: False
        '''
        def _size_format(size, fmt):
            if fmt is None:
                return size

            fmt = fmt.lower()
            if fmt == "b":
                return "{0} Bytes".format(size)
            elif fmt == "kb":
                return "{0} Kb".format(round((float(size) / 0x400), 2))
            elif fmt == "mb":
                return "{0} Mb".format(round((float(size) / 0x400 / 0x400), 2))
            elif fmt == "gb":
                return "{0} Gb".format(round((float(size) / 0x400 / 0x400 / 0x400), 2))

        filter = None
        if 'filter' in kwargs:
            filter = kwargs['filter']

        timeformat = kwargs.get("time", "tz")
        if timeformat not in ["ticks", "tz"]:
            raise InspectorQueryException('Unknown "{0}" value for parameter "time"'.format(timeformat))
        tfmt = lambda param: timeformat == "tz" and time.strftime("%b %d %Y %H:%M:%S", time.gmtime(param)) or int(param)

        size_fmt = kwargs.get("size")
        if size_fmt is not None and size_fmt.lower() not in ["b", "kb", "mb", "gb"]:
            raise InspectorQueryException('Unknown "{0}" value for parameter "size". '
                                          'Should be either B, Kb, Mb or Gb'.format(timeformat))

        owners = kwargs.get("owners", "id")
        if owners not in ["name", "id"]:
            raise InspectorQueryException('Unknown "{0}" value for parameter "owners". '
                                          'Should be either name or id (default)'.format(owners))

        incl_type = [prm for prm in kwargs.get("type", "").lower().split(",") if prm]
        if not incl_type:
            incl_type.append("file")

        for i_type in incl_type:
            if i_type not in ["directory", "dir", "d", "file", "f", "link", "l"]:
                raise InspectorQueryException('Unknown "{0}" values for parameter "type". '
                                              'Should be comma separated one or more of '
                                              'dir, file and/or link.'.format(", ".join(incl_type)))

        where_clause = set()
        for i_type in incl_type:
            if i_type in ["file", "f"]:
                where_clause.add("p_type = 'f'")
            elif i_type in ["d", "dir", "directory"]:
                where_clause.add("p_type = 'd'")
            elif i_type in ["l", "link"]:
                where_clause.add("p_type = 'l'")

        if filter:
            where_filter_clause = " AND path LIKE '{0}%'".format(filter)
        else:
            where_filter_clause = ""

        self.db.open()
        self.db.cursor.execute("SELECT id, path, p_type, mode, uid, gid, p_size, atime, mtime, ctime "
                               "FROM inspector_payload "
                               "WHERE {0}{1}".format(" OR ".join(list(where_clause)),
                                                     where_filter_clause))

        brief = kwargs.get("brief")
        if brief:
            data = list()
        else:
            data = dict()

        for pld_data in self.db.cursor.fetchall():
            p_id, path, p_type, mode, uid, gid, p_size, atime, mtime, ctime = pld_data
            if brief:
                data.append(path)
            else:
                data[path] = {
                    'uid': self._id_resolv(uid, named=(owners == "id")),
                    'gid': self._id_resolv(gid, named=(owners == "id"), uid=False),
                    'size': _size_format(p_size, fmt=size_fmt),
                    'mode': oct(mode),
                    'accessed': tfmt(atime),
                    'modified': tfmt(mtime),
                    'created': tfmt(ctime),
                }

        self.db.close()

        return data

    def _all(self, *args, **kwargs):
        '''
        Return all the summary of the particular system.
        '''
        data = dict()
        data['software'] = self._software(**kwargs)
        data['system'] = self._system(**kwargs)
        data['services'] = self._services(**kwargs)
        try:
            data['configuration'] = self._configuration(**kwargs)
        except InspectorQueryException as ex:
            data['configuration'] = 'N/A'
            log.error(ex)
        data['payload'] = self._payload(**kwargs) or 'N/A'

        return data
コード例 #2
0
ファイル: query.py プロジェクト: mjura/salt-1
class Query(object):
    '''
    Query the system.
    This class is actually puts all Salt features together,
    so there would be no need to pick it from various places.
    '''

    # Configuration: config files
    # Identity: users/groups
    # Software: packages, patterns, repositories
    # Services
    # System: distro, RAM etc
    # Changes: all files that are managed and were changed from the original
    # all: include all scopes (scary!)
    # payload: files that are not managed

    SCOPES = [
        "changes", "configuration", "identity", "system", "software",
        "services", "payload", "all"
    ]

    def __init__(self, scope):
        '''
        Constructor.

        :param scope:
        :return:
        '''
        if scope not in self.SCOPES:
            raise InspectorQueryException(
                "Unknown scope: {0}. Must be one of: {1}".format(
                    repr(scope), ", ".join(self.SCOPES)))
        self.scope = '_' + scope
        self.db = DBHandle(globals()['__salt__']['config.get']('inspector.db',
                                                               ''))
        self.local_identity = dict()

    def __call__(self, *args, **kwargs):
        '''
        Call the query with the defined scope.

        :param args:
        :param kwargs:
        :return:
        '''

        return getattr(self, self.scope)(*args, **kwargs)

    def _changes(self, *args, **kwargs):
        '''
        Returns all diffs to the configuration files.
        '''
        raise Exception("Not yet implemented")

    def _configuration(self, *args, **kwargs):
        '''
        Return configuration files.
        '''

        data = dict()
        self.db.open()
        self.db.cursor.execute("SELECT id, name FROM inspector_pkg")
        for pkg_id, pkg_name in self.db.cursor.fetchall():
            self.db.cursor.execute(
                "SELECT id, path FROM inspector_pkg_cfg_files WHERE pkgid=?",
                (pkg_id, ))
            configs = list()
            for cnf_id, cnf_name in self.db.cursor.fetchall():
                configs.append(cnf_name)
            data[pkg_name] = configs
        self.db.close()

        if not data:
            raise InspectorQueryException(
                "No inspected configuration yet available.")

        return data

    def _get_local_users(self, disabled=None):
        '''
        Return all known local accounts to the system.
        '''
        users = dict()
        path = '/etc/passwd'
        with salt.utils.fopen(path, 'r') as fp_:
            for line in fp_:
                line = line.strip()
                if ':' not in line:
                    continue
                name, password, uid, gid, gecos, directory, shell = line.split(
                    ':')
                active = not (password == '*' or password.startswith('!'))
                if (disabled is False
                        and active) or (disabled is True
                                        and not active) or disabled is None:
                    users[name] = {
                        'uid': uid,
                        'git': gid,
                        'info': gecos,
                        'home': directory,
                        'shell': shell,
                        'disabled': not active
                    }

        return users

    def _get_local_groups(self):
        '''
        Return all known local groups to the system.
        '''
        groups = dict()
        path = '/etc/group'
        with salt.utils.fopen(path, 'r') as fp_:
            for line in fp_:
                line = line.strip()
                if ':' not in line:
                    continue
                name, password, gid, users = line.split(':')
                groups[name] = {
                    'gid': gid,
                }

                if users:
                    groups[name]['users'] = users.split(',')

        return groups

    def _get_external_accounts(self, locals):
        '''
        Return all known accounts, excluding local accounts.
        '''
        users = dict()
        out = __salt__['cmd.run_all']("passwd -S -a")
        if out['retcode']:
            # System does not supports all accounts descriptions, just skipping.
            return users
        status = {
            'L': 'Locked',
            'NP': 'No password',
            'P': 'Usable password',
            'LK': 'Locked'
        }
        for data in [
                elm.strip().split(" ")
                for elm in out['stdout'].split(os.linesep) if elm.strip()
        ]:
            if len(data) < 2:
                continue
            name, login = data[:2]
            if name not in locals:
                users[name] = {
                    'login': login,
                    'status': status.get(login, 'N/A')
                }

        return users

    def _identity(self, *args, **kwargs):
        '''
        Local users and groups.

        accounts
            Can be either 'local', 'remote' or 'all' (equal to "local,remote").
            Remote accounts cannot be resolved on all systems, but only
            those, which supports 'passwd -S -a'.

        disabled
            True (or False, default) to return only disabled accounts.
        '''
        LOCAL = 'local accounts'
        EXT = 'external accounts'

        data = dict()
        data[LOCAL] = self._get_local_users(disabled=kwargs.get('disabled'))
        data[EXT] = self._get_external_accounts(data[LOCAL].keys()) or 'N/A'
        data['local groups'] = self._get_local_groups()

        return data

    def _system(self, *args, **kwargs):
        '''
        This basically calls grains items and picks out only
        necessary information in a certain structure.

        :param args:
        :param kwargs:
        :return:
        '''
        sysinfo = SysInfo(__grains__.get("kernel"))

        data = dict()
        data['cpu'] = sysinfo._get_cpu()
        data['disks'] = sysinfo._get_fs()
        data['mounts'] = sysinfo._get_mounts()
        data['memory'] = sysinfo._get_mem()
        data['network'] = sysinfo._get_network()
        data['os'] = sysinfo._get_os()

        return data

    def _software(self, *args, **kwargs):
        '''
        Return installed software.
        '''
        data = dict()
        if 'exclude' in kwargs:
            excludes = kwargs['exclude'].split(",")
        else:
            excludes = list()

        os_family = __grains__.get("os_family").lower()

        # Get locks
        if os_family == 'suse':
            LOCKS = "pkg.list_locks"
            if 'products' not in excludes:
                products = __salt__['pkg.list_products']()
                if products:
                    data['products'] = products
        elif os_family == 'redhat':
            LOCKS = "pkg.get_locked_packages"
        else:
            LOCKS = None

        if LOCKS and 'locks' not in excludes:
            locks = __salt__[LOCKS]()
            if locks:
                data['locks'] = locks

        # Get patterns
        if os_family == 'suse':
            PATTERNS = 'pkg.list_installed_patterns'
        elif os_family == 'redhat':
            PATTERNS = 'pkg.group_list'
        else:
            PATTERNS = None

        if PATTERNS and 'patterns' not in excludes:
            patterns = __salt__[PATTERNS]()
            if patterns:
                data['patterns'] = patterns

        # Get packages
        if 'packages' not in excludes:
            data['packages'] = __salt__['pkg.list_pkgs']()

        # Get repositories
        if 'repositories' not in excludes:
            repos = __salt__['pkg.list_repos']()
            if repos:
                data['repositories'] = repos

        return data

    def _services(self, *args, **kwargs):
        '''
        Get list of enabled and disabled services on the particular system.
        '''
        return {
            'enabled': __salt__['service.get_enabled'](),
            'disabled': __salt__['service.get_disabled'](),
        }

    def _id_resolv(self, iid, named=True, uid=True):
        '''
        Resolve local users and groups.

        :param iid:
        :param named:
        :param uid:
        :return:
        '''

        if not self.local_identity:
            self.local_identity['users'] = self._get_local_users()
            self.local_identity['groups'] = self._get_local_groups()

        if not named:
            return iid

        for name, meta in self.local_identity[uid and 'users'
                                              or 'groups'].items():
            if (uid and int(meta.get('uid', -1)) == iid) or (not uid and int(
                    meta.get('gid', -1)) == iid):
                return name

        return iid

    def _payload(self, *args, **kwargs):
        '''
        Find all unmanaged files.

        Parameters:

        * **filter**: Include only results which path starts from the filter string.
        * **time**: Display time in Unix ticks or format according to the configured TZ (default)
                    Values: ticks, tz (default)
        * **size**: Format size. Values: B, KB, MB, GB
        * **owners**: Resolve UID/GID to an actual names or leave them numeric (default).
                      Values: name (default), id
        * **type**: Comma-separated type of included payload: dir (or directory), link and/or file.
        * **brief**: Return just a list of matches, if True. Default: False
        '''
        def _size_format(size, fmt):
            if fmt is None:
                return size

            fmt = fmt.lower()
            if fmt == "b":
                return "{0} Bytes".format(size)
            elif fmt == "kb":
                return "{0} Kb".format(round((float(size) / 0x400), 2))
            elif fmt == "mb":
                return "{0} Mb".format(round((float(size) / 0x400 / 0x400), 2))
            elif fmt == "gb":
                return "{0} Gb".format(
                    round((float(size) / 0x400 / 0x400 / 0x400), 2))

        filter = None
        if 'filter' in kwargs:
            filter = kwargs['filter']

        timeformat = kwargs.get("time", "tz")
        if timeformat not in ["ticks", "tz"]:
            raise InspectorQueryException(
                'Unknown "{0}" value for parameter "time"'.format(timeformat))
        tfmt = lambda param: timeformat == "tz" and time.strftime(
            "%b %d %Y %H:%M:%S", time.gmtime(param)) or int(param)

        size_fmt = kwargs.get("size")
        if size_fmt is not None and size_fmt.lower() not in [
                "b", "kb", "mb", "gb"
        ]:
            raise InspectorQueryException(
                'Unknown "{0}" value for parameter "size". '
                'Should be either B, Kb, Mb or Gb'.format(timeformat))

        owners = kwargs.get("owners", "id")
        if owners not in ["name", "id"]:
            raise InspectorQueryException(
                'Unknown "{0}" value for parameter "owners". '
                'Should be either name or id (default)'.format(owners))

        incl_type = [
            prm for prm in kwargs.get("type", "").lower().split(",") if prm
        ]
        if not incl_type:
            incl_type.append("file")

        for i_type in incl_type:
            if i_type not in [
                    "directory", "dir", "d", "file", "f", "link", "l"
            ]:
                raise InspectorQueryException(
                    'Unknown "{0}" values for parameter "type". '
                    'Should be comma separated one or more of '
                    'dir, file and/or link.'.format(", ".join(incl_type)))

        where_clause = set()
        for i_type in incl_type:
            if i_type in ["file", "f"]:
                where_clause.add("p_type = 'f'")
            elif i_type in ["d", "dir", "directory"]:
                where_clause.add("p_type = 'd'")
            elif i_type in ["l", "link"]:
                where_clause.add("p_type = 'l'")

        if filter:
            where_filter_clause = " AND path LIKE '{0}%'".format(filter)
        else:
            where_filter_clause = ""

        self.db.open()
        self.db.cursor.execute(
            "SELECT id, path, p_type, mode, uid, gid, p_size, atime, mtime, ctime "
            "FROM inspector_payload "
            "WHERE {0}{1}".format(" OR ".join(list(where_clause)),
                                  where_filter_clause))

        brief = kwargs.get("brief")
        if brief:
            data = list()
        else:
            data = dict()

        for pld_data in self.db.cursor.fetchall():
            p_id, path, p_type, mode, uid, gid, p_size, atime, mtime, ctime = pld_data
            if brief:
                data.append(path)
            else:
                data[path] = {
                    'uid': self._id_resolv(uid, named=(owners == "id")),
                    'gid': self._id_resolv(gid,
                                           named=(owners == "id"),
                                           uid=False),
                    'size': _size_format(p_size, fmt=size_fmt),
                    'mode': oct(mode),
                    'accessed': tfmt(atime),
                    'modified': tfmt(mtime),
                    'created': tfmt(ctime),
                }

        self.db.close()

        return data

    def _all(self, *args, **kwargs):
        '''
        Return all the summary of the particular system.
        '''
        data = dict()
        data['software'] = self._software(**kwargs)
        data['system'] = self._system(**kwargs)
        data['services'] = self._services(**kwargs)
        try:
            data['configuration'] = self._configuration(**kwargs)
        except InspectorQueryException as ex:
            data['configuration'] = 'N/A'
            log.error(ex)
        data['payload'] = self._payload(**kwargs) or 'N/A'

        return data