glances_processlist.py 10.4 KB
Newer Older
A
Alessio Sergi 已提交
1 2
# -*- coding: utf-8 -*-
#
3
# This file is part of Glances.
A
Alessio Sergi 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
#
# 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/>.

N
Nicolas Hennion 已提交
20
# Import sys libs
21
import os
A
Alessio Sergi 已提交
22 23
from datetime import timedelta

N
Nicolas Hennion 已提交
24
# Import Glances libs
N
Nicolas Hennion 已提交
25
from glances.core.glances_globals import glances_processes
A
Alessio Sergi 已提交
26
from glances.plugins.glances_plugin import GlancesPlugin
A
Alessio Sergi 已提交
27 28 29 30 31 32 33 34 35


class Plugin(GlancesPlugin):
    """
    Glances's processes Plugin

    stats is a list
    """

36 37
    def __init__(self, args=None):
        GlancesPlugin.__init__(self, args=args)
A
Alessio Sergi 已提交
38 39 40 41 42 43 44 45 46 47

        # We want to display the stat in the curse interface
        self.display_curse = True
        # Set the message position
        # It is NOT the curse position but the Glances column/line
        # Enter -1 to right align
        self.column_curse = 1
        # Enter -1 to diplay bottom
        self.line_curse = 4

48
        # Note: 'glances_processes' is already init in the glances_processes.py script
49

50
    def reset(self):
51
        """
52
        Reset/init the stats
53
        """
54
        self.stats = []
55 56 57

    def update(self):
        """
58
        Update processes stats using the input method
59 60
        """

61 62
        # Reset stats
        self.reset()
63

64
        if self.get_input() == 'local':
65 66 67 68
            # Update stats using the standard system lib
            # Note: Update is done in the processcount plugin
            # Just return the processes list
            self.stats = glances_processes.getlist()
69
        elif self.get_input() == 'snmp':
70 71 72
            # Update stats using SNMP
            # !!! TODO
            pass
73

74
        return self.stats
75

A
Alessio Sergi 已提交
76 77 78 79 80 81 82 83
    def msg_curse(self, args=None):
        """
        Return the dict to display in the curse interface
        """

        # Init the return message
        ret = []

84
        # Only process if stats exist and display plugin enable...
85
        if self.stats == [] or args.disable_process:
86 87
            return ret

A
Alessio Sergi 已提交
88
        # Compute the sort key
89 90 91
        try:
            args.process_sorted_by
        except AttributeError:
A
Alessio Sergi 已提交
92
            args.process_sorted_by = glances_processes.getsortkey()
93
        if args.process_sorted_by == 'auto':
N
Nicolas Hennion 已提交
94
            process_sort_key = glances_processes.getsortkey()
A
Alessio Sergi 已提交
95 96
        else:
            process_sort_key = args.process_sorted_by
97
        sort_style = 'SORT'
A
Alessio Sergi 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111

        # Header
        msg = "{0:>6}".format(_("CPU%"))
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
        msg = "{0:>6}".format(_("MEM%"))
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
        msg = "{0:>6}".format(_("VIRT"))
        ret.append(self.curse_add_line(msg, optional=True))
        msg = "{0:>6}".format(_("RES"))
        ret.append(self.curse_add_line(msg, optional=True))
        msg = "{0:>6}".format(_("PID"))
        ret.append(self.curse_add_line(msg, optional=True))
        msg = " {0:10}".format(_("USER"))
        ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
112
        msg = "{0:>4}".format(_("NI"))
A
Alessio Sergi 已提交
113
        ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
114
        msg = "{0:>2}".format(_("S"))
A
Alessio Sergi 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128
        ret.append(self.curse_add_line(msg, optional=True))
        msg = "{0:>9}".format(_("TIME+"))
        ret.append(self.curse_add_line(msg, optional=True))
        msg = "{0:>6}".format(_("IOR/s"))
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True))
        msg = "{0:>6}".format(_("IOW/s"))
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True))
        msg = " {0:8}".format(_("Command"))
        ret.append(self.curse_add_line(msg, optional=True))

        # Trying to display proc time
        tag_proc_time = True

        # Loop over processes (sorted by the sort key previously compute)
129
        for p in self.sortlist(process_sort_key):
A
Alessio Sergi 已提交
130 131
            ret.append(self.curse_new_line())
            # CPU
A
Alessio Sergi 已提交
132
            msg = "{0:>6.1f}".format(p['cpu_percent'])
A
Alessio Sergi 已提交
133 134 135
            ret.append(self.curse_add_line(msg,
                                           self.get_alert(p['cpu_percent'], header="cpu")))
            # MEM
A
Alessio Sergi 已提交
136
            msg = "{0:>6.1f}".format(p['memory_percent'])
A
Alessio Sergi 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
            ret.append(self.curse_add_line(msg,
                                           self.get_alert(p['memory_percent'], header="mem")))
            # VMS
            msg = "{0:>6}".format(self.auto_unit(p['memory_info'][1], low_precision=False))
            ret.append(self.curse_add_line(msg, optional=True))
            # RSS
            msg = "{0:>6}".format(self.auto_unit(p['memory_info'][0], low_precision=False))
            ret.append(self.curse_add_line(msg, optional=True))
            # PID
            msg = "{0:>6}".format(p['pid'])
            ret.append(self.curse_add_line(msg, optional=True))
            # USER
            msg = " {0:9}".format(p['username'][:9])
            ret.append(self.curse_add_line(msg, optional=True))
            # NICE
152
            nice = p['nice']
A
Alessio Sergi 已提交
153
            msg = "{0:>5}".format(nice)
154 155 156 157
            if nice != 0:
                ret.append(self.curse_add_line(msg, decoration='NICE', optional=True))
            else:
                ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
158
            # STATUS
159
            status = p['status']
A
Alessio Sergi 已提交
160
            msg = "{0:>2}".format(status)
161
            if status == 'R':
162
                ret.append(self.curse_add_line(msg, decoration='STATUS', optional=True))
163 164
            else:
                ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
165
            # TIME+
166
            if tag_proc_time:
A
Alessio Sergi 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180
                try:
                    dtime = timedelta(seconds=sum(p['cpu_times']))
                except Exception:
                    # Catched on some Amazon EC2 server
                    # See https://github.com/nicolargo/glances/issues/87
                    tag_proc_time = False
                else:
                    msg = "{0}:{1}.{2}".format(str(dtime.seconds // 60 % 60),
                                               str(dtime.seconds % 60).zfill(2),
                                               str(dtime.microseconds)[:2].zfill(2))
            else:
                msg = " "
            msg = "{0:>9}".format(msg)
            ret.append(self.curse_add_line(msg, optional=True))
N
Nicolas Hennion 已提交
181
            # IO read/write
182
            if 'io_counters' in p:
N
Nicolas Hennion 已提交
183 184
                # IO read
                io_rs = (p['io_counters'][0] - p['io_counters'][2]) / p['time_since_update']
185
                if io_rs == 0:
N
Nicolas Hennion 已提交
186 187 188 189 190 191
                    msg = "{0:>6}".format("0")
                else:
                    msg = "{0:>6}".format(self.auto_unit(io_rs, low_precision=False))
                ret.append(self.curse_add_line(msg, optional=True))
                # IO write
                io_ws = (p['io_counters'][1] - p['io_counters'][3]) / p['time_since_update']
192
                if io_ws == 0:
N
Nicolas Hennion 已提交
193 194 195 196
                    msg = "{0:>6}".format("0")
                else:
                    msg = "{0:>6}".format(self.auto_unit(io_ws, low_precision=False))
                ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
197
            else:
N
Nicolas Hennion 已提交
198 199 200
                msg = "{0:>6}".format("?")
                ret.append(self.curse_add_line(msg, optional=True))
                ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
201
            # Command line
202 203 204 205 206 207 208 209 210 211 212 213 214 215
            # If no command line for the process is available, fallback to
            # the bare process name instead
            cmdline = p['cmdline']
            if cmdline == "":
                msg = " {0}".format(p['name'])
                ret.append(self.curse_add_line(msg, optional=True, splittable=True))
            else:
                try:
                    cmd = cmdline.split()[0]
                    args = ' '.join(cmdline.split()[1:])
                    path, basename = os.path.split(cmd)
                    if os.path.isdir(path):
                        msg = " {0}".format(path) + os.sep
                        ret.append(self.curse_add_line(msg, optional=True, splittable=True))
216
                        ret.append(self.curse_add_line(basename, decoration='PROCESS', optional=True, splittable=True))
217 218
                    else:
                        msg = " {0}".format(basename)
219 220 221
                        ret.append(self.curse_add_line(msg, decoration='PROCESS', optional=True, splittable=True))
                    msg = " {0}".format(args)
                    ret.append(self.curse_add_line(msg, optional=True, splittable=True))
222 223
                except UnicodeEncodeError:
                    ret.append(self.curse_add_line("", optional=True, splittable=True))
A
Alessio Sergi 已提交
224 225 226

        # Return the message with decoration
        return ret
227 228 229 230 231

    def sortlist(self, sortedby=None):
        """
        Return the self.stats sorted by sortedby
        """
232
        if sortedby is None:
233 234 235 236
            # No need to sort...
            return self.stats

        sortedreverse = True
237
        if sortedby == 'name':
238 239
            sortedreverse = False

240
        if sortedby == 'io_counters':
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
            # Specific case for io_counters
            # Sum of io_r + io_w
            try:
                # Sort process by IO rate (sum IO read + IO write)
                listsorted = sorted(self.stats,
                                    key=lambda process: process[sortedby][0] -
                                    process[sortedby][2] + process[sortedby][1] -
                                    process[sortedby][3],
                                    reverse=sortedreverse)
            except Exception:
                listsorted = sorted(self.stats,
                                    key=lambda process: process['cpu_percent'],
                                    reverse=sortedreverse)
        else:
            # Others sorts
            listsorted = sorted(self.stats,
                                key=lambda process: process[sortedby],
                                reverse=sortedreverse)

        self.stats = listsorted

A
Alessio Sergi 已提交
262
        return self.stats