amps_list.py 6.3 KB
Newer Older
N
nicolargo 已提交
1 2 3 4
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
N
nicolargo 已提交
5
# Copyright (C) 2018 Nicolargo <nicolas@nicolargo.com>
N
nicolargo 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Manage the AMPs list."""

import os
import re
N
nicolargo 已提交
24
import threading
N
nicolargo 已提交
25 26 27 28 29 30 31 32 33

from glances.compat import listkeys, iteritems
from glances.logger import logger
from glances.globals import amps_path
from glances.processes import glances_processes


class AmpsList(object):

N
nicolargo 已提交
34
    """This class describes the optional application monitoring process list.
N
nicolargo 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

    The AMP list is a list of processes with a specific monitoring action.

    The list (Python list) is composed of items (Python dict).
    An item is defined (dict keys):
    *...
    """

    # The dict
    __amps_dict = {}

    def __init__(self, args, config):
        """Init the AMPs list."""
        self.args = args
        self.config = config

51
        # Load the AMP configurations / scripts
N
nicolargo 已提交
52 53
        self.load_configs()

54 55
    def load_configs(self):
        """Load the AMP configuration files."""
56 57 58
        if self.config is None:
            return False

59 60 61 62
        # Display a warning (deprecated) message if the monitor section exist
        if "monitor" in self.config.sections():
            logger.warning("A deprecated [monitor] section exists in the Glances configuration file. You should use the new Applications Monitoring Process module instead (http://glances.readthedocs.io/en/develop/aoa/amps.html).")

N
nicolargo 已提交
63
        header = "glances_"
64 65 66 67 68 69 70 71 72 73
        # For each AMP scrip, call the load_config method
        for s in self.config.sections():
            if s.startswith("amp_"):
                # An AMP section exists in the configuration file
                # If an AMP script exist in the glances/amps folder, use it
                amp_conf_name = s[4:]
                amp_script = os.path.join(amps_path, header + s[4:] + ".py")
                if not os.path.exists(amp_script):
                    # If not, use the default script
                    amp_script = os.path.join(amps_path, "glances_default.py")
74
                try:
75
                    amp = __import__(os.path.basename(amp_script)[:-3])
76
                except ImportError as e:
77
                    logger.warning("Missing Python Lib ({}), cannot load {} AMP".format(e, amp_conf_name))
78
                except Exception as e:
79
                    logger.warning("Cannot load {} AMP ({})".format(amp_conf_name, e))
80 81 82 83 84
                else:
                    # Add the AMP to the dictionary
                    # The key is the AMP name
                    # for example, the file glances_xxx.py
                    # generate self._amps_list["xxx"] = ...
85 86 87
                    self.__amps_dict[amp_conf_name] = amp.Amp(name=amp_conf_name, args=self.args)
                    # Load the AMP configuration
                    self.__amps_dict[amp_conf_name].load_config(self.config)
N
nicolargo 已提交
88
        # Log AMPs list
89
        logger.debug("AMPs list: {}".format(self.getList()))
N
nicolargo 已提交
90

91 92
        return True

N
nicolargo 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106
    def __str__(self):
        return str(self.__amps_dict)

    def __repr__(self):
        return self.__amps_dict

    def __getitem__(self, item):
        return self.__amps_dict[item]

    def __len__(self):
        return len(self.__amps_dict)

    def update(self):
        """Update the command result attributed."""
107
        # Get the current processes list (once)
108
        processlist = glances_processes.getlist()
109

N
nicolargo 已提交
110 111
        # Iter upon the AMPs dict
        for k, v in iteritems(self.get()):
112 113 114
            if not v.enable():
                # Do not update if the enable tag is set
                continue
115 116 117

            amps_list = self._build_amps_list(v, processlist)

N
nicolargo 已提交
118 119
            if len(amps_list) > 0:
                # At least one process is matching the regex
120 121 122
                logger.debug("AMPS: {} processes {} detected ({})".format(len(amps_list),
                                                                          k,
                                                                          amps_list))
N
nicolargo 已提交
123
                # Call the AMP update method
124
                thread = threading.Thread(target=v.update_wrapper, args=[amps_list])
N
nicolargo 已提交
125
                thread.start()
126 127 128
            else:
                # Set the process number to 0
                v.set_count(0)
129
                if v.count_min() is not None and v.count_min() > 0:
130
                    # Only display the "No running process message" if countmin is defined
131
                    v.set_result("No running process")
N
nicolargo 已提交
132 133 134

        return self.__amps_dict

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    def _build_amps_list(self, amp_value, processlist):
        """Return the AMPS process list according to the amp_value

        Search application monitored processes by a regular expression
        """
        ret = []
        try:
            # Search in both cmdline and name (for kernel thread, see #1261)
            for p in processlist:
                add_it = False
                if (re.search(amp_value.regex(), p['name']) is not None):
                    add_it = True
                else:
                    for c in p['cmdline']:
                        if (re.search(amp_value.regex(), c) is not None):
                            add_it = True
                            break
                if add_it:
                    ret.append({'pid': p['pid'],
                                'cpu_percent': p['cpu_percent'],
                                'memory_percent': p['memory_percent']})

        except (TypeError, KeyError) as e:
            logger.debug("Can not build AMPS list ({})".format(e))

        return ret

N
nicolargo 已提交
162 163 164 165 166 167 168 169 170 171 172
    def getList(self):
        """Return the AMPs list."""
        return listkeys(self.__amps_dict)

    def get(self):
        """Return the AMPs dict."""
        return self.__amps_dict

    def set(self, new_dict):
        """Set the AMPs dict."""
        self.__amps_dict = new_dict