glances.py 172.9 KB
Newer Older
A
asergi 已提交
1
#!/usr/bin/env python
A
asergi 已提交
2
# -*- coding: utf-8 -*-
A
asergi 已提交
3
#
A
Alessio Sergi 已提交
4
# Glances - An eye on your system
A
asergi 已提交
5
#
6
# Copyright (C) 2013 Nicolargo <nicolas@nicolargo.com>
A
asergi 已提交
7
#
A
asergi 已提交
8 9 10
# 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
A
asergi 已提交
11 12
# (at your option) any later version.
#
A
asergi 已提交
13 14 15 16
# 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.
A
asergi 已提交
17 18
#
# You should have received a copy of the GNU Lesser General Public License
A
asergi 已提交
19
# along with this program. If not, see <http://www.gnu.org/licenses/>.
A
asergi 已提交
20 21

__appname__ = 'glances'
N
Nicolas Hennion 已提交
22
__version__ = "1.7.3"
A
asergi 已提交
23 24 25 26 27 28
__author__ = "Nicolas Hennion <nicolas@nicolargo.com>"
__licence__ = "LGPL"

# Libraries
#==========

N
Nicolas Hennion 已提交
29
# Standards libs
A
asergi 已提交
30 31 32 33 34 35 36
import os
import sys
import platform
import getopt
import signal
import time
from datetime import datetime, timedelta
37 38
import re
import subprocess
N
Nicolas Hennion 已提交
39
import locale
A
asergi 已提交
40
import gettext
M
MendelGusmao 已提交
41
import socket
N
Nicolas Hennion 已提交
42

N
Nicolas Hennion 已提交
43
# Specifics libs
A
asergi 已提交
44 45
import json
import collections
N
Nicolas Hennion 已提交
46

A
asergi 已提交
47 48 49 50
# For client/server authentication
from base64 import b64decode
from hashlib import md5

51
# Somes libs depends of OS
A
asergi 已提交
52
is_BSD = sys.platform.find('bsd') != -1
53 54 55 56
is_Linux = sys.platform.startswith('linux')
is_Mac = sys.platform.startswith('darwin')
is_Windows = sys.platform.startswith('win')

57
try:
A
asergi 已提交
58
    # Python 2
59 60
    from ConfigParser import RawConfigParser
    from ConfigParser import NoOptionError
A
asergi 已提交
61 62 63 64
except ImportError:
    # Python 3
    from configparser import RawConfigParser
    from configparser import NoOptionError
65

N
Nicolas Hennion 已提交
66
try:
A
asergi 已提交
67
    # Python 2
N
Nicolas Hennion 已提交
68
    from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
N
Nicolas Hennion 已提交
69
    from SimpleXMLRPCServer import SimpleXMLRPCServer
A
asergi 已提交
70
except ImportError:
A
asergi 已提交
71
    # Python 3
N
Nicolas Hennion 已提交
72
    from xmlrpc.server import SimpleXMLRPCRequestHandler
N
Nicolas Hennion 已提交
73
    from xmlrpc.server import SimpleXMLRPCServer
A
asergi 已提交
74 75

try:
A
asergi 已提交
76
    # Python 2
77
    from xmlrpclib import ServerProxy, ProtocolError
A
asergi 已提交
78
except ImportError:
A
asergi 已提交
79
    # Python 3
80
    from xmlrpc.client import ServerProxy, ProtocolError
N
Nicolas Hennion 已提交
81

A
asergi 已提交
82
if not is_Windows:
A
asergi 已提交
83
    # curses did not exist on Windows (shame on it)
N
Nicolas Hennion 已提交
84 85 86 87
    try:
        import curses
        import curses.panel
    except ImportError:
88
        print('Curses module not found. Glances cannot start.')
A
asergi 已提交
89
        sys.exit(1)
N
Nicolas Hennion 已提交
90

F
fraoustin 已提交
91 92
if is_Windows:
    try:
93 94
        import colorconsole
        import colorconsole.terminal
A
Alessio Sergi 已提交
95 96 97 98
    except ImportError:
        is_colorConsole = False
    else:
        is_colorConsole = True
F
fraoustin 已提交
99

N
Nicolas Hennion 已提交
100
try:
A
Alessio Sergi 已提交
101
    # psutil is the main library used to grab stats
N
Nicolas Hennion 已提交
102 103
    import psutil
except ImportError:
104
    print('PsUtil module not found. Glances cannot start.')
N
Nicolas Hennion 已提交
105
    sys.exit(1)
A
asergi 已提交
106

A
asergi 已提交
107
psutil_version = tuple([int(num) for num in psutil.__version__.split('.')])
108 109
# this is not a mistake: psutil 0.5.1 is detected as 0.5.0
if psutil_version < (0, 5, 0):
110 111
    print('PsUtil version %s detected.' % psutil.__version__)
    print('PsUtil 0.5.1 or higher is needed. Glances cannot start.')
N
Nicolas Hennion 已提交
112
    sys.exit(1)
A
asergi 已提交
113

N
Nicolas Hennion 已提交
114
try:
A
Alessio Sergi 已提交
115
    # psutil.virtual_memory() only available from psutil >= 0.6
A
asergi 已提交
116
    psutil.virtual_memory()
N
Nicolas Hennion 已提交
117
except Exception:
A
asergi 已提交
118
    psutil_mem_vm = False
N
Nicolas Hennion 已提交
119
else:
A
asergi 已提交
120
    psutil_mem_vm = True
N
Nicolas Hennion 已提交
121

A
Alessio Sergi 已提交
122 123 124 125 126 127 128 129
try:
    # psutil.net_io_counters() only available from psutil >= 1.0.0
    psutil.net_io_counters()
except Exception:
    psutil_net_io_counters = False
else:
    psutil_net_io_counters = True

A
asergi 已提交
130
if not is_Mac:
A
asergi 已提交
131
    psutil_get_io_counter_tag = True
A
asergi 已提交
132
else:
A
asergi 已提交
133
    # get_io_counters() not available on OS X
A
asergi 已提交
134
    psutil_get_io_counter_tag = False
N
Nicolas Hennion 已提交
135

A
Alessio Sergi 已提交
136
# sensors library (optional; Linux-only)
A
asergi 已提交
137 138 139 140 141 142 143 144
if is_Linux:
    try:
        import sensors
    except ImportError:
        sensors_lib_tag = False
    else:
        sensors_lib_tag = True
else:
145 146
    sensors_lib_tag = False

A
Alessio Sergi 已提交
147 148 149 150 151 152 153 154
# batinfo library (optional; Linux-only)
if is_Linux:
    try:
        import batinfo
    except ImportError:
        batinfo_lib_tag = False
    else:
        batinfo_lib_tag = True
A
Alessio Sergi 已提交
155 156
else:
    batinfo_lib_tag = False
A
Alessio Sergi 已提交
157

158
try:
A
asergi 已提交
159
    # HTML output (optional)
N
Nicolas Hennion 已提交
160 161 162 163 164 165 166
    import jinja2
except ImportError:
    html_lib_tag = False
else:
    html_lib_tag = True

try:
A
asergi 已提交
167
    # CSV output (optional)
N
Nicolas Hennion 已提交
168 169 170 171 172 173
    import csv
except ImportError:
    cvs_lib_tag = False
else:
    csv_lib_tag = True

A
Alessio Sergi 已提交
174
# path definitions
A
Alessio Sergi 已提交
175
work_path = os.path.realpath(os.path.dirname(__file__))
A
Alessio Sergi 已提交
176
appname_path = os.path.split(sys.argv[0])[0]
A
Alessio Sergi 已提交
177
sys_prefix = os.path.realpath(os.path.dirname(appname_path))
A
Alessio Sergi 已提交
178

A
Alessio Sergi 已提交
179 180 181 182 183
# i18n
locale.setlocale(locale.LC_ALL, '')
gettext_domain = __appname__

# get locale directory
A
Alessio Sergi 已提交
184
i18n_path = os.path.realpath(os.path.join(work_path, '..', 'i18n'))
A
Alessio Sergi 已提交
185
sys_i18n_path = os.path.join(sys_prefix, 'share', 'locale')
A
Alessio Sergi 已提交
186 187 188

if os.path.exists(i18n_path):
    locale_dir = i18n_path
A
Alessio Sergi 已提交
189 190
elif os.path.exists(sys_i18n_path):
    locale_dir = sys_i18n_path
A
Alessio Sergi 已提交
191 192 193 194
else:
    locale_dir = None
gettext.install(gettext_domain, locale_dir)

195 196
# Default tag
sensors_tag = False
M
MendelGusmao 已提交
197
hddtemp_tag = False
198 199 200
network_tag = True
diskio_tag = True
fs_tag = True
201
process_tag = True
202

203 204 205
# Global moved outside main for unit tests
last_update_times = {}

A
asergi 已提交
206 207 208

# Classes
#========
A
Alessio Sergi 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
if is_Windows and is_colorConsole:
    import msvcrt
    import threading
    try:
        import Queue as queue
    except ImportError:
        import queue

    class ListenGetch(threading.Thread):

        def __init__(self, nom=''):
            threading.Thread.__init__(self)
            self.Terminated = False
            self.q = queue.Queue()

        def run(self):
            while not self.Terminated:
                char = msvcrt.getch()
                self.q.put(char)

        def stop(self):
            self.Terminated = True
            while not self.q.empty():
                self.q.get()

        def get(self, default=None):
            try:
                return ord(self.q.get_nowait())
            except Exception:
                return default

    class Screen():

242
        COLOR_DEFAULT_WIN = '0F'  # 07'#'0F'
A
Alessio Sergi 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
        COLOR_BK_DEFAULT = colorconsole.terminal.colors["BLACK"]
        COLOR_FG_DEFAULT = colorconsole.terminal.colors["WHITE"]

        def __init__(self, nc):
            self.nc = nc
            self.term = colorconsole.terminal.get_terminal()
            # os.system('color %s' % self.COLOR_DEFAULT_WIN)
            self.listen = ListenGetch()
            self.listen.start()

            self.term.clear()

        def subwin(self, x, y):
            return self

        def keypad(self, id):
            return None

        def nodelay(self, id):
            return None

        def getch(self):
            return self.listen.get(27)

        def erase(self):
            self.reset()
            return None

        def addnstr(self, y, x, msg, ln, typo=0):
            try:
                fgs, bks = self.nc.colors[typo]
            except Exception:
                fgs, bks = self.COLOR_FG_DEFAULT, self.COLOR_BK_DEFAULT
            self.term.set_color(fg=fgs, bk=bks)
            self.term.print_at(x, y, msg.ljust(ln))
            self.term.set_color(fg=self.COLOR_FG_DEFAULT, bk=self.COLOR_BK_DEFAULT)

        def getmaxyx(self):
            x = (self.term._Terminal__get_console_info().srWindow.Right -
                 self.term._Terminal__get_console_info().srWindow.Left + 1)
            y = (self.term._Terminal__get_console_info().srWindow.Bottom -
                 self.term._Terminal__get_console_info().srWindow.Top + 1)
            return [y, x]

        def reset(self):
            self.term.clear()
            self.term.reset()
            return None

        def restore_buffered_mode(self):
            self.term.restore_buffered_mode()
            return None

    class WCurseLight():

        COLOR_WHITE = colorconsole.terminal.colors["WHITE"]
        COLOR_RED = colorconsole.terminal.colors["RED"]
        COLOR_GREEN = colorconsole.terminal.colors["GREEN"]
        COLOR_BLUE = colorconsole.terminal.colors["LBLUE"]
        COLOR_MAGENTA = colorconsole.terminal.colors["LPURPLE"]
        COLOR_BLACK = colorconsole.terminal.colors["BLACK"]
        A_UNDERLINE = 0
        A_BOLD = 0
        COLOR_PAIRS = 9
        colors = {}

        def __init__(self):
            self.term = Screen(self)

        def initscr(self):
            return self.term

        def start_color(self):
            return None

        def use_default_colors(self):
            return None

        def noecho(self):
            return None

        def cbreak(self):
            return None

        def curs_set(self, y):
            return None

        def has_colors(self):
            return True

        def echo(self):
            return None

        def nocbreak(self):
            return None

        def endwin(self):
            self.term.reset()
            self.term.restore_buffered_mode()
            self.term.listen.stop()

        def napms(self, t):
            time.sleep(t / 1000 if t > 1000 else 1)

        def init_pair(self, id, fg, bk):
            self.colors[id] = [max(fg, 0), max(bk, 0)]

        def color_pair(self, id):
            return id

    curses = WCurseLight()

A
asergi 已提交
355 356 357 358 359

class Timer:
    """
    The timer class
    """
360

A
asergi 已提交
361 362 363 364 365 366 367 368 369 370
    def __init__(self, duration):
        self.started(duration)

    def started(self, duration):
        self.target = time.time() + duration

    def finished(self):
        return time.time() > self.target


A
asergi 已提交
371 372 373 374 375 376
class Config:
    """
    This class is used to access/read config file, if it exists

    :param location: the custom path to search for config file
    :type location: str or None
A
asergi 已提交
377
    """
378

A
asergi 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391
    def __init__(self, location=None):
        self.location = location
        self.filename = 'glances.conf'

        self.parser = RawConfigParser()
        self.load()

    def load(self):
        """
        Load a config file from the list of paths, if it exists
        """
        for path in self.get_paths_list():
            if os.path.isfile(path) and os.path.getsize(path) > 0:
392 393 394 395 396 397 398 399 400
                try:
                    if sys.version_info >= (3, 2):
                        self.parser.read(path, encoding='utf-8')
                    else:
                        self.parser.read(path)
                except UnicodeDecodeError as e:
                    print(_("Error decoding config file '%s': %s") % (path, e))
                    sys.exit(1)

A
asergi 已提交
401 402 403 404 405 406 407
                break

    def get_paths_list(self):
        """
        Get a list of config file paths, taking into account of the OS,
        priority and location.

A
Alessio Sergi 已提交
408
        * running from source: /path/to/glances/conf
A
asergi 已提交
409 410 411
        * Linux: ~/.config/glances, /etc/glances
        * BSD: ~/.config/glances, /usr/local/etc/glances
        * Mac: ~/Library/Application Support/glances, /usr/local/etc/glances
412
        * Windows: %APPDATA%\glances
A
asergi 已提交
413 414 415

        The config file will be searched in the following order of priority:
            * /path/to/file (via -C flag)
A
Alessio Sergi 已提交
416
            * /path/to/glances/conf
A
asergi 已提交
417
            * user's home directory (per-user settings)
A
Alessio Sergi 已提交
418
            * {/usr/local,}/etc directory (system-wide settings)
A
asergi 已提交
419 420
        """
        paths = []
A
Alessio Sergi 已提交
421
        conf_path = os.path.realpath(os.path.join(work_path, '..', 'conf'))
A
asergi 已提交
422 423 424 425

        if self.location is not None:
            paths.append(self.location)

A
Alessio Sergi 已提交
426
        if os.path.exists(conf_path):
A
Alessio Sergi 已提交
427
            paths.append(os.path.join(conf_path, self.filename))
A
Alessio Sergi 已提交
428

A
asergi 已提交
429 430 431 432 433 434 435 436
        if is_Linux or is_BSD:
            paths.append(os.path.join(
                os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'),
                __appname__, self.filename))
        elif is_Mac:
            paths.append(os.path.join(
                os.path.expanduser('~/Library/Application Support/'),
                __appname__, self.filename))
437 438 439
        elif is_Windows:
            paths.append(os.path.join(
                os.environ.get('APPDATA'), __appname__, self.filename))
A
asergi 已提交
440 441 442

        if is_Linux:
            paths.append(os.path.join('/etc', __appname__, self.filename))
A
Alessio Sergi 已提交
443
        elif is_BSD:
A
asergi 已提交
444
            paths.append(os.path.join(
A
Alessio Sergi 已提交
445 446 447 448
                sys.prefix, 'etc', __appname__, self.filename))
        elif is_Mac:
            paths.append(os.path.join(
                sys_prefix, 'etc', __appname__, self.filename))
A
asergi 已提交
449 450 451 452 453 454 455 456 457 458 459

        return paths

    def has_section(self, section):
        """
        Return info about the existence of a section
        """
        return self.parser.has_section(section)

    def get_option(self, section, option):
        """
460
        Get the float value of an option, if it exists
A
asergi 已提交
461 462
        """
        try:
463
            value = self.parser.getfloat(section, option)
A
asergi 已提交
464 465
        except NoOptionError:
            return
466 467
        else:
            return value
A
asergi 已提交
468

469 470
    def get_raw_option(self, section, option):
        """
471
        Get the raw value of an option, if it exists
472 473 474 475 476 477 478 479
        """
        try:
            value = self.parser.get(section, option)
        except NoOptionError:
            return
        else:
            return value

480

481 482
class monitorList:
    """
483 484 485
    This class describes the optionnal monitored processes list
    A list of 'important' processes to monitor.

N
Nicolas Hennion 已提交
486
    The list (Python list) is composed of items (Python dict)
487 488 489
    An item is defined (Dict keys'):
    * description: Description of the processes (max 16 chars)
    * regex: regular expression of the processes to monitor
490
    * command: (optional) shell command for extended stat
491 492
    * countmin: (optional) minimal number of processes
    * countmax: (optional) maximum number of processes
493
    """
494 495 496
    # Maximum number of items in the list
    __monitor_list_max_size = 10
    # The list
497 498 499 500 501
    __monitor_list = []

    def __init__(self):
        if config.has_section('monitor'):
            # Process monitoring list
502
            self.__setMonitorList('monitor', 'list')
503

504
    def __setMonitorList(self, section, key):
505
        """
506
        Init the monitored processes list
N
Nicolas Hennion 已提交
507
        The list is defined in the Glances configuration file
508
        """
509 510
        for l in range(1, self.__monitor_list_max_size + 1):
            value = {}
511
            key = "list_" + str(l) + "_"
512 513 514 515 516 517
            try:
                description = config.get_raw_option(section, key + "description")
                regex = config.get_raw_option(section, key + "regex")
                command = config.get_raw_option(section, key + "command")
                countmin = config.get_raw_option(section, key + "countmin")
                countmax = config.get_raw_option(section, key + "countmax")
518
            except Exception:
519 520
                pass
            else:
521
                if description is not None and regex is not None:
522 523 524 525 526 527 528 529
                    # Build the new item
                    value["description"] = description
                    value["regex"] = regex
                    value["command"] = command
                    value["countmin"] = countmin
                    value["countmax"] = countmax
                    # Add the item to the list
                    self.__monitor_list.append(value)
530 531 532 533 534 535 536 537 538 539 540 541 542

    def __str__(self):
        return str(self.__monitor_list)

    def __repr__(self):
        return self.__monitor_list

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

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

543 544 545 546 547
    def __get__(self, item, key):
        """
        Meta function to return key value of item
        None if not defined or item > len(list)
        """
548
        if item < len(self.__monitor_list):
549 550
            try:
                return self.__monitor_list[item][key]
551
            except Exception:
552 553 554 555
                return None
        else:
            return None

556 557 558 559 560 561
    def getAll(self):
        return self.__monitor_list

    def setAll(self, newlist):
        self.__monitor_list = newlist

562 563 564 565 566
    def description(self, item):
        """
        Return the description of the item number (item)
        """
        return self.__get__(item, "description")
567 568

    def regex(self, item):
569 570 571 572
        """
        Return the regular expression of the item number (item)
        """
        return self.__get__(item, "regex")
573 574

    def command(self, item):
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
        """
        Return the stats command of the item number (item)
        """
        return self.__get__(item, "command")

    def countmin(self, item):
        """
        Return the minimum number of processes of the item number (item)
        """
        return self.__get__(item, "countmin")

    def countmax(self, item):
        """
        Return the maximum number of processes of the item number (item)
        """
        return self.__get__(item, "countmax")
591

A
asergi 已提交
592 593

class glancesLimits:
A
asergi 已提交
594
    """
A
asergi 已提交
595
    Manage the limit OK, CAREFUL, WARNING, CRITICAL for each stats
A
asergi 已提交
596

A
asergi 已提交
597 598 599 600 601 602
    The limit list is stored in an hash table:
    __limits_list[STAT] = [CAREFUL, WARNING, CRITICAL]

    STD is for defaults limits (CPU/MEM/SWAP/FS)
    CPU_IOWAIT limits (iowait in %)
    LOAD is for LOAD limits (5 min/15 min)
603 604
    TEMP is for sensors limits (temperature in °C)
    HDDTEMP is for hddtemp limits (temperature in °C)
A
asergi 已提交
605
    """
A
asergi 已提交
606
    __limits_list = {'STD': [50, 70, 90],
607 608 609
                     'CPU_USER': [50, 70, 90],
                     'CPU_SYSTEM': [50, 70, 90],
                     'CPU_IOWAIT': [40, 60, 80],
N
Nicolas Hennion 已提交
610
                     'LOAD': [0.7, 1.0, 5.0],
611 612 613
                     'MEM': [50, 70, 90],
                     'SWAP': [50, 70, 90],
                     'TEMP': [60, 70, 80],
614
                     'HDDTEMP': [45, 52, 60],
N
Nicolas Hennion 已提交
615 616 617
                     'FS': [50, 70, 90],
                     'PROCESS_CPU': [50, 70, 90],
                     'PROCESS_MEM': [50, 70, 90]}
618

A
asergi 已提交
619 620 621 622
    def __init__(self):
        # Test if the configuration file has a limits section
        if config.has_section('global'):
            # Read STD limits
623 624 625
            self.__setLimits('STD', 'global', 'careful')
            self.__setLimits('STD', 'global', 'warning')
            self.__setLimits('STD', 'global', 'critical')
A
asergi 已提交
626 627
        if config.has_section('cpu'):
            # Read CPU limits
628 629 630 631 632 633 634 635 636
            self.__setLimits('CPU_USER', 'cpu', 'user_careful')
            self.__setLimits('CPU_USER', 'cpu', 'user_warning')
            self.__setLimits('CPU_USER', 'cpu', 'user_critical')
            self.__setLimits('CPU_SYSTEM', 'cpu', 'system_careful')
            self.__setLimits('CPU_SYSTEM', 'cpu', 'system_warning')
            self.__setLimits('CPU_SYSTEM', 'cpu', 'system_critical')
            self.__setLimits('CPU_IOWAIT', 'cpu', 'iowait_careful')
            self.__setLimits('CPU_IOWAIT', 'cpu', 'iowait_warning')
            self.__setLimits('CPU_IOWAIT', 'cpu', 'iowait_critical')
A
asergi 已提交
637 638
        if config.has_section('load'):
            # Read LOAD limits
639 640 641
            self.__setLimits('LOAD', 'load', 'careful')
            self.__setLimits('LOAD', 'load', 'warning')
            self.__setLimits('LOAD', 'load', 'critical')
A
asergi 已提交
642 643
        if config.has_section('memory'):
            # Read MEM limits
644 645 646
            self.__setLimits('MEM', 'memory', 'careful')
            self.__setLimits('MEM', 'memory', 'warning')
            self.__setLimits('MEM', 'memory', 'critical')
A
asergi 已提交
647 648
        if config.has_section('swap'):
            # Read MEM limits
649 650 651
            self.__setLimits('SWAP', 'swap', 'careful')
            self.__setLimits('SWAP', 'swap', 'warning')
            self.__setLimits('SWAP', 'swap', 'critical')
A
asergi 已提交
652 653
        if config.has_section('temperature'):
            # Read TEMP limits
654 655 656
            self.__setLimits('TEMP', 'temperature', 'careful')
            self.__setLimits('TEMP', 'temperature', 'warning')
            self.__setLimits('TEMP', 'temperature', 'critical')
657 658
        if config.has_section('hddtemperature'):
            # Read HDDTEMP limits
659 660 661
            self.__setLimits('HDDTEMP', 'hddtemperature', 'careful')
            self.__setLimits('HDDTEMP', 'hddtemperature', 'warning')
            self.__setLimits('HDDTEMP', 'hddtemperature', 'critical')
A
asergi 已提交
662 663
        if config.has_section('filesystem'):
            # Read FS limits
664 665 666
            self.__setLimits('FS', 'filesystem', 'careful')
            self.__setLimits('FS', 'filesystem', 'warning')
            self.__setLimits('FS', 'filesystem', 'critical')
A
asergi 已提交
667 668
        if config.has_section('process'):
            # Process limits
669 670 671 672 673 674
            self.__setLimits('PROCESS_CPU', 'process', 'cpu_careful')
            self.__setLimits('PROCESS_CPU', 'process', 'cpu_warning')
            self.__setLimits('PROCESS_CPU', 'process', 'cpu_critical')
            self.__setLimits('PROCESS_MEM', 'process', 'mem_careful')
            self.__setLimits('PROCESS_MEM', 'process', 'mem_warning')
            self.__setLimits('PROCESS_MEM', 'process', 'mem_critical')
A
asergi 已提交
675

676
    def __setLimits(self, stat, section, alert):
677
        """
A
asergi 已提交
678 679 680 681
        stat: 'CPU', 'LOAD', 'MEM', 'SWAP', 'TEMP', etc.
        section: 'cpu', 'load', 'memory', 'swap', 'temperature', etc.
        alert: 'careful', 'warning', 'critical'
        """
682
        value = config.get_option(section, alert)
A
asergi 已提交
683 684

        #~ print("%s / %s = %s -> %s" % (section, alert, value, stat))
685 686 687 688 689 690
        if alert.endswith('careful'):
            self.__limits_list[stat][0] = value
        elif alert.endswith('warning'):
            self.__limits_list[stat][1] = value
        elif alert.endswith('critical'):
            self.__limits_list[stat][2] = value
691

692 693 694 695 696 697 698
    def setAll(self, newlimits):
        self.__limits_list = newlimits
        return True

    def getAll(self):
        return self.__limits_list

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
    def getCareful(self, stat):
        return self.__limits_list[stat][0]

    def getWarning(self, stat):
        return self.__limits_list[stat][1]

    def getCritical(self, stat):
        return self.__limits_list[stat][2]

    # TO BE DELETED AFTER THE HTML output refactoring
    def getSTDCareful(self):
        return self.getCareful('STD')

    def getSTDWarning(self):
        return self.getWarning('STD')

    def getSTDCritical(self):
        return self.getCritical('STD')
    # /TO BE DELETED AFTER THE HTML output refactoring

    def getCPUCareful(self, stat):
        return self.getCareful('CPU_' + stat.upper())

    def getCPUWarning(self, stat):
        return self.getWarning('CPU_' + stat.upper())

    def getCPUCritical(self, stat):
        return self.getCritical('CPU_' + stat.upper())

    def getLOADCareful(self, core=1):
        return self.getCareful('LOAD') * core

    def getLOADWarning(self, core=1):
        return self.getWarning('LOAD') * core

    def getLOADCritical(self, core=1):
        return self.getCritical('LOAD') * core

    def getMEMCareful(self):
        return self.getCareful('MEM')

    def getMEMWarning(self):
        return self.getWarning('MEM')

    def getMEMCritical(self):
        return self.getCritical('MEM')

    def getSWAPCareful(self):
        return self.getCareful('SWAP')

    def getSWAPWarning(self):
        return self.getWarning('SWAP')

    def getSWAPCritical(self):
        return self.getCritical('SWAP')

    def getTEMPCareful(self):
        return self.getCareful('TEMP')

    def getTEMPWarning(self):
        return self.getWarning('TEMP')

    def getTEMPCritical(self):
        return self.getCritical('TEMP')

    def getHDDTEMPCareful(self):
        return self.getCareful('HDDTEMP')

    def getHDDTEMPWarning(self):
        return self.getWarning('HDDTEMP')

    def getHDDTEMPCritical(self):
        return self.getCritical('HDDTEMP')

    def getFSCareful(self):
        return self.getCareful('FS')

    def getFSWarning(self):
        return self.getWarning('FS')

    def getFSCritical(self):
        return self.getCritical('FS')

    def getProcessCareful(self, stat='', core=1):
        if stat.upper() != 'CPU':
            # Use core only for CPU
            core = 1
        return self.getCareful('PROCESS_' + stat.upper()) * core

    def getProcessWarning(self, stat='', core=1):
        if stat.upper() != 'CPU':
            # Use core only for CPU
            core = 1
        return self.getWarning('PROCESS_' + stat.upper()) * core

    def getProcessCritical(self, stat='', core=1):
        if stat.upper() != 'CPU':
            # Use core only for CPU
            core = 1
        return self.getCritical('PROCESS_' + stat.upper()) * core

A
asergi 已提交
800 801 802 803

class glancesLogs:
    """
    The main class to manage logs inside the Glances software
804 805
    Logs is a list of list (stored in the self.logs_list var)
    See item description in the add function
A
asergi 已提交
806
    """
807

A
asergi 已提交
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
    def __init__(self):
        """
        Init the logs classe
        """
        # Maximum size of the logs list
        self.logs_max = 10

        # Init the logs list
        self.logs_list = []

    def get(self):
        """
        Return the logs list (RAW)
        """
        return self.logs_list

    def len(self):
        """
        Return the number of item in the log list
        """
        return self.logs_list.__len__()

    def __itemexist__(self, item_type):
        """
        An item exist in the list if:
        * end is < 0
        * item_type is matching
        """
836
        for i in range(self.len()):
A
asergi 已提交
837
            if self.logs_list[i][1] < 0 and self.logs_list[i][3] == item_type:
A
asergi 已提交
838 839 840
                return i
        return -1

841
    def add(self, item_state, item_type, item_value, proc_list=[], proc_desc=""):
A
asergi 已提交
842 843
        """
        item_state = "OK|CAREFUL|WARNING|CRITICAL"
844
        item_type = "CPU*|LOAD|MEM|MON"
A
asergi 已提交
845 846 847
        item_value = value
        Item is defined by:
          ["begin", "end", "WARNING|CRITICAL", "CPU|LOAD|MEM",
848
           MAX, AVG, MIN, SUM, COUNT,
849 850
           [top3 process list],
           "Processes description"]
A
asergi 已提交
851 852 853 854 855
        If item is a 'new one':
          Add the new item at the beginning of the logs list
        Else:
          Update the existing item
        """
N
Nicolas Hennion 已提交
856
        # Add Top process sort depending on alert type
857
        sortby = 'none'
A
asergi 已提交
858
        if item_type.startswith("MEM"):
859
            # Sort TOP process by memory_percent
860
            sortby = 'memory_percent'
861 862 863
        elif item_type.startswith("CPU IO") and is_Linux:
            # Sort TOP process by io_counters (only for Linux OS)
            sortby = 'io_counters'
864 865 866
        elif item_type.startswith("MON"):
            # Do no sort process for monitored prcesses list
            sortby = 'none'
N
Nicolas Hennion 已提交
867
        else:
868
            # Default TOP process sort is cpu_percent
N
Nicolas Hennion 已提交
869
            sortby = 'cpu_percent'
870 871

        # Sort processes
872
        if sortby != 'none':
873 874 875 876
            topprocess = sorted(proc_list, key=lambda process: process[sortby],
                                reverse=True)
        else:
            topprocess = proc_list
N
Nicolas Hennion 已提交
877 878

        # Add or update the log
A
asergi 已提交
879 880 881
        item_index = self.__itemexist__(item_type)
        if item_index < 0:
            # Item did not exist, add if WARNING or CRITICAL
A
asergi 已提交
882
            if item_state == "WARNING" or item_state == "CRITICAL":
A
asergi 已提交
883 884 885
                # Time is stored in Epoch format
                # Epoch -> DMYHMS = datetime.fromtimestamp(epoch)
                item = []
886
                # START DATE
A
asergi 已提交
887
                item.append(time.mktime(datetime.now().timetuple()))
888
                # END DATE
A
asergi 已提交
889
                item.append(-1)
A
asergi 已提交
890 891 892 893 894 895 896 897
                item.append(item_state)       # STATE: WARNING|CRITICAL
                item.append(item_type)        # TYPE: CPU, LOAD, MEM...
                item.append(item_value)       # MAX
                item.append(item_value)       # AVG
                item.append(item_value)       # MIN
                item.append(item_value)       # SUM
                item.append(1)                # COUNT
                item.append(topprocess[0:3])  # TOP 3 PROCESS LIST
898
                item.append(proc_desc)        # MONITORED PROCESSES DESC
A
asergi 已提交
899 900 901 902 903
                self.logs_list.insert(0, item)
                if self.len() > self.logs_max:
                    self.logs_list.pop()
        else:
            # Item exist, update
A
asergi 已提交
904
            if item_state == "OK" or item_state == "CAREFUL":
A
asergi 已提交
905 906 907
                # Close the item
                self.logs_list[item_index][1] = time.mktime(
                    datetime.now().timetuple())
N
Nicolas Hennion 已提交
908 909
                # TOP PROCESS LIST
                self.logs_list[item_index][9] = []
A
asergi 已提交
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
            else:
                # Update the item
                # State
                if item_state == "CRITICAL":
                    self.logs_list[item_index][2] = item_state
                # Value
                if item_value > self.logs_list[item_index][4]:
                    # MAX
                    self.logs_list[item_index][4] = item_value
                elif item_value < self.logs_list[item_index][6]:
                    # MIN
                    self.logs_list[item_index][6] = item_value
                # AVG
                self.logs_list[item_index][7] += item_value
                self.logs_list[item_index][8] += 1
925 926
                self.logs_list[item_index][5] = (self.logs_list[item_index][7] /
                                                 self.logs_list[item_index][8])
N
Nicolas Hennion 已提交
927 928
                # TOP PROCESS LIST
                self.logs_list[item_index][9] = topprocess[0:3]
929 930
                # MONITORED PROCESSES DESC
                self.logs_list[item_index][10] = proc_desc
A
asergi 已提交
931 932 933

        return self.len()

A
asergi 已提交
934
    def clean(self, critical=False):
935 936 937 938 939 940 941
        """
        Clean the log list by deleting finished item
        By default, only delete WARNING message
        If critical = True, also delete CRITICAL message
        """
        # Create a new clean list
        clean_logs_list = []
A
asergi 已提交
942
        while self.len() > 0:
943
            item = self.logs_list.pop()
A
asergi 已提交
944
            if item[1] < 0 or (not critical and item[2] == "CRITICAL"):
945 946 947 948
                clean_logs_list.insert(0, item)
        # The list is now the clean one
        self.logs_list = clean_logs_list
        return self.len()
A
asergi 已提交
949

950

A
asergi 已提交
951 952 953 954
class glancesGrabFs:
    """
    Get FS stats
    """
955

A
asergi 已提交
956 957 958 959 960
    def __init__(self):
        """
        Init FS stats
        """
        # Ignore the following FS name
A
asergi 已提交
961 962
        self.ignore_fsname = ('', 'cgroup', 'fusectl', 'gvfs-fuse-daemon',
                              'gvfsd-fuse', 'none')
A
asergi 已提交
963 964

        # Ignore the following FS type
A
asergi 已提交
965 966 967
        self.ignore_fstype = ('autofs', 'binfmt_misc', 'configfs', 'debugfs',
                              'devfs', 'devpts', 'devtmpfs', 'hugetlbfs',
                              'iso9660', 'linprocfs', 'mqueue', 'none',
A
Alessio Sergi 已提交
968 969
                              'proc', 'procfs', 'pstore', 'rootfs',
                              'securityfs', 'sysfs', 'usbfs')
A
asergi 已提交
970 971 972

        # ignore FS by mount point
        self.ignore_mntpoint = ('', '/dev/shm', '/lib/init/rw', '/sys/fs/cgroup')
A
asergi 已提交
973 974 975 976 977 978 979 980 981

    def __update__(self):
        """
        Update the stats
        """
        # Reset the list
        self.fs_list = []

        # Open the current mounted FS
A
asergi 已提交
982
        fs_stat = psutil.disk_partitions(all=True)
983
        for fs in range(len(fs_stat)):
A
asergi 已提交
984 985 986 987 988 989 990 991
            fs_current = {}
            fs_current['device_name'] = fs_stat[fs].device
            if fs_current['device_name'] in self.ignore_fsname:
                continue
            fs_current['fs_type'] = fs_stat[fs].fstype
            if fs_current['fs_type'] in self.ignore_fstype:
                continue
            fs_current['mnt_point'] = fs_stat[fs].mountpoint
A
asergi 已提交
992 993
            if fs_current['mnt_point'] in self.ignore_mntpoint:
                continue
A
asergi 已提交
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
            try:
                fs_usage = psutil.disk_usage(fs_current['mnt_point'])
            except Exception:
                continue
            fs_current['size'] = fs_usage.total
            fs_current['used'] = fs_usage.used
            fs_current['avail'] = fs_usage.free
            self.fs_list.append(fs_current)

    def get(self):
        self.__update__()
        return self.fs_list


1008 1009
class glancesGrabSensors:
    """
A
asergi 已提交
1010
    Get sensors stats using the PySensors library
1011
    """
1012

1013 1014 1015 1016
    def __init__(self):
        """
        Init sensors stats
        """
N
Nicolas Hennion 已提交
1017 1018
        try:
            sensors.init()
1019
        except Exception:
N
Nicolas Hennion 已提交
1020 1021 1022
            self.initok = False
        else:
            self.initok = True
1023 1024 1025 1026 1027 1028 1029 1030

    def __update__(self):
        """
        Update the stats
        """
        # Reset the list
        self.sensors_list = []

A
asergi 已提交
1031
        # grab only temperature stats
N
Nicolas Hennion 已提交
1032 1033 1034 1035
        if self.initok:
            for chip in sensors.iter_detected_chips():
                for feature in chip:
                    sensors_current = {}
A
asergi 已提交
1036 1037 1038 1039
                    if feature.name.startswith('temp'):
                        sensors_current['label'] = feature.label[:20]
                        sensors_current['value'] = int(feature.get_value())
                        self.sensors_list.append(sensors_current)
1040 1041 1042 1043 1044 1045

    def get(self):
        self.__update__()
        return self.sensors_list

    def quit(self):
N
Nicolas Hennion 已提交
1046 1047
        if self.initok:
            sensors.cleanup()
1048

A
Alessio Sergi 已提交
1049

M
MendelGusmao 已提交
1050 1051 1052
class glancesGrabHDDTemp:
    """
    Get hddtemp stats using a socket connection
A
Alessio Sergi 已提交
1053
    """
1054 1055 1056 1057
    cache = ""
    address = "127.0.0.1"
    port = 7634

M
MendelGusmao 已提交
1058 1059 1060 1061 1062 1063
    def __init__(self):
        """
        Init hddtemp stats
        """
        try:
            sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1064
            sck.connect((self.address, self.port))
M
MendelGusmao 已提交
1065
            sck.close()
1066
        except Exception:
M
MendelGusmao 已提交
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
            self.initok = False
        else:
            self.initok = True

    def __update__(self):
        """
        Update the stats
        """
        # Reset the list
        self.hddtemp_list = []

        if self.initok:
1079
            data = ""
1080
            # Taking care of sudden deaths/stops of hddtemp daemon
1081 1082 1083 1084 1085
            try:
                sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sck.connect((self.address, self.port))
                data = sck.recv(4096)
                sck.close()
1086
            except Exception:
M
MendelGusmao 已提交
1087
                hddtemp_current = {}
1088 1089 1090 1091 1092 1093
                hddtemp_current['label'] = "hddtemp is gone"
                hddtemp_current['value'] = 0
                self.hddtemp_list.append(hddtemp_current)
                return
            else:
                # Considering the size of "|/dev/sda||0||" as the minimum
A
Alessio Sergi 已提交
1094
                if len(data) < 14:
1095 1096 1097 1098 1099
                    if len(self.cache) == 0:
                        data = "|hddtemp error||0||"
                    else:
                        data = self.cache
                self.cache = data
1100 1101
                fields = data.decode('utf-8').split("|")
                devices = (len(fields) - 1) // 5
1102 1103
                for i in range(0, devices):
                    offset = i * 5
1104
                    hddtemp_current = {}
M
MendelGusmao 已提交
1105 1106
                    temperature = fields[offset + 3]
                    if temperature == "ERR":
1107
                        hddtemp_current['label'] = _("hddtemp error")
M
MendelGusmao 已提交
1108
                        hddtemp_current['value'] = 0
1109 1110
                    elif temperature == "SLP":
                        hddtemp_current['label'] = fields[offset + 1].split("/")[-1] + " is sleeping"
1111
                        hddtemp_current['value'] = 0
1112 1113
                    elif temperature == "UNK":
                        hddtemp_current['label'] = fields[offset + 1].split("/")[-1] + " is unknown"
1114
                        hddtemp_current['value'] = 0
M
MendelGusmao 已提交
1115 1116
                    else:
                        hddtemp_current['label'] = fields[offset + 1].split("/")[-1]
1117 1118 1119 1120 1121
                        try:
                            hddtemp_current['value'] = int(temperature)
                        except TypeError:
                            hddtemp_current['label'] = fields[offset + 1].split("/")[-1] + " is unknown"
                            hddtemp_current['value'] = 0
M
MendelGusmao 已提交
1122
                    self.hddtemp_list.append(hddtemp_current)
M
MendelGusmao 已提交
1123 1124 1125 1126 1127

    def get(self):
        self.__update__()
        return self.hddtemp_list

A
asergi 已提交
1128

1129 1130 1131 1132
class GlancesGrabProcesses:
    """
    Get processed stats using the PsUtil lib
    """
1133

1134 1135 1136 1137 1138 1139 1140
    def __init__(self):
        """
        Init the io dict
        key = pid
        value = [ read_bytes_old, write_bytes_old ]
        """
        self.io_old = {}
1141

A
asergi 已提交
1142
    def __get_process_stats(self, proc):
1143
        """
A
asergi 已提交
1144
        Get process statistics
1145 1146
        """
        procstat = {}
A
asergi 已提交
1147

A
asergi 已提交
1148
        procstat['name'] = proc.name
1149
        procstat['pid'] = proc.pid
P
Piotr Skamruk 已提交
1150 1151 1152
        try:
            procstat['username'] = proc.username
        except KeyError:
1153 1154 1155 1156
            try:
                procstat['username'] = proc.uids.real
            except KeyError:
                procstat['username'] = "?"
1157
        procstat['cmdline'] = ' '.join(proc.cmdline)
A
asergi 已提交
1158 1159 1160 1161
        procstat['memory_info'] = proc.get_memory_info()
        procstat['memory_percent'] = proc.get_memory_percent()
        procstat['status'] = str(proc.status)[:1].upper()
        procstat['cpu_times'] = proc.get_cpu_times()
A
asergi 已提交
1162
        procstat['cpu_percent'] = proc.get_cpu_percent(interval=0)
1163
        procstat['nice'] = proc.get_nice()
1164

1165 1166 1167 1168 1169 1170 1171
        # try:
        #     # !!! High CPU consumption
        #     procstat['tcp'] = len(proc.get_connections(kind="tcp"))
        #     procstat['udp'] = len(proc.get_connections(kind="udp"))
        # except:
        #     procstat['tcp'] = 0
        #     procstat['udp'] = 0
1172

1173
        # procstat['io_counters'] is a list:
A
asergi 已提交
1174 1175
        # [read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag]
        # If io_tag = 0 > Access denied (display "?")
1176 1177 1178 1179 1180
        # If io_tag = 1 > No access denied (display the IO rate)
        if psutil_get_io_counter_tag:
            try:
                # Get the process IO counters
                proc_io = proc.get_io_counters()
A
asergi 已提交
1181
                io_new = [proc_io.read_bytes, proc_io.write_bytes]
1182 1183 1184
            except psutil.AccessDenied:
                # Access denied to process IO (no root account)
                # Put 0 in all values (for sort) and io_tag = 0 (for display)
A
asergi 已提交
1185
                procstat['io_counters'] = [0, 0] + [0, 0]
1186 1187 1188 1189 1190 1191 1192
                io_tag = 0
            else:
                # For IO rate computation
                # Append saved IO r/w bytes
                try:
                    procstat['io_counters'] = io_new + self.io_old[procstat['pid']]
                except KeyError:
A
asergi 已提交
1193
                    procstat['io_counters'] = io_new + [0, 0]
1194 1195 1196 1197 1198
                # then save the IO r/w bytes
                self.io_old[procstat['pid']] = io_new
                io_tag = 1

            # Append the IO tag (for display)
A
asergi 已提交
1199
            procstat['io_counters'] += [io_tag]
1200 1201 1202 1203 1204 1205

        return procstat

    def update(self):
        self.processlist = []
        self.processcount = {'total': 0, 'running': 0, 'sleeping': 0}
A
asergi 已提交
1206

1207
        time_since_update = getTimeSinceLastUpdate('process_disk')
1208
        # For each existing process...
1209 1210
        for proc in psutil.process_iter():
            try:
A
asergi 已提交
1211
                procstat = self.__get_process_stats(proc)
1212
                procstat['time_since_update'] = time_since_update
A
asergi 已提交
1213 1214 1215
                # ignore the 'idle' process on Windows and *BSD
                # ignore the 'kernel_task' process on OS X
                # waiting for upstream patch from psutil
A
asergi 已提交
1216
                if (is_BSD and procstat['name'] == 'idle' or
A
asergi 已提交
1217 1218
                    is_Windows and procstat['name'] == 'System Idle Process' or
                    is_Mac and procstat['name'] == 'kernel_task'):
1219 1220 1221 1222 1223 1224 1225
                    continue
                # Update processcount (global stattistics)
                try:
                    self.processcount[str(proc.status)] += 1
                except KeyError:
                    # Key did not exist, create it
                    self.processcount[str(proc.status)] = 1
A
asergi 已提交
1226 1227 1228
                else:
                    self.processcount['total'] += 1
            except (psutil.NoSuchProcess, psutil.AccessDenied):
1229
                continue
A
asergi 已提交
1230 1231 1232
            else:
                # Update processlist
                self.processlist.append(procstat)
1233 1234 1235 1236 1237 1238 1239 1240

    def getcount(self):
        return self.processcount

    def getlist(self):
        return self.processlist


1241 1242 1243 1244
class glancesGrabBat:
    """
    Get batteries stats using the Batinfo librairie
    """
1245

1246 1247 1248 1249
    def __init__(self):
        """
        Init batteries stats
        """
A
Alessio Sergi 已提交
1250 1251 1252
        if batinfo_lib_tag:
            try:
                self.bat = batinfo.batteries()
1253 1254
                self.initok = True
                self.__update__()
1255
            except Exception:
A
Alessio Sergi 已提交
1256 1257
                self.initok = False
        else:
1258
            self.initok = False
1259 1260 1261 1262 1263 1264

    def __update__(self):
        """
        Update the stats
        """
        if self.initok:
1265 1266
            try:
                self.bat.update()
1267
            except Exception:
1268 1269 1270
                self.bat_list = []
            else:
                self.bat_list = self.bat.stat
1271
        else:
A
Alessio Sergi 已提交
1272
            self.bat_list = []
1273 1274

    def get(self):
A
Alessio Sergi 已提交
1275
        # Update the stats
1276 1277 1278 1279
        self.__update__()
        return self.bat_list

    def getcapacitypercent(self):
A
Alessio Sergi 已提交
1280 1281 1282 1283 1284 1285
        if not self.initok or self.bat_list == []:
            return []
        # Init the bsum (sum of percent) and bcpt (number of batteries)
        # and Loop over batteries (yes a computer could have more than 1 battery)
        bsum = 0
        for bcpt in range(len(self.get())):
N
Nicolas Hennion 已提交
1286 1287 1288 1289
            try:
                bsum = bsum + int(self.bat_list[bcpt].capacity)
            except ValueError:
                return []
A
Alessio Sergi 已提交
1290 1291 1292
        bcpt = bcpt + 1
        # Return the global percent
        return int(bsum / bcpt)
1293 1294


1295
class GlancesStats:
A
asergi 已提交
1296 1297 1298
    """
    This class store, update and give stats
    """
1299

1300
    def __init__(self):
A
asergi 已提交
1301 1302 1303
        """
        Init the stats
        """
1304
        self._init_host()
A
Alessio Sergi 已提交
1305

1306 1307 1308 1309 1310
        # Init the grab error tags
        # for managing error during stats grab
        # By default, we *hope* that there is no error
        self.network_error_tag = False
        self.diskio_error_tag = False
1311

A
asergi 已提交
1312 1313 1314 1315 1316
        # Init the fs stats
        try:
            self.glancesgrabfs = glancesGrabFs()
        except Exception:
            self.glancesgrabfs = {}
1317

A
asergi 已提交
1318
        # Init the sensors stats (optional)
N
Nicolas Hennion 已提交
1319
        if sensors_tag:
1320 1321
            try:
                self.glancesgrabsensors = glancesGrabSensors()
A
asergi 已提交
1322
            except Exception:
1323
                self.sensors_tag = False
A
asergi 已提交
1324

M
MendelGusmao 已提交
1325 1326 1327 1328 1329 1330 1331
        # Init the hddtemp stats (optional)
        if hddtemp_tag:
            try:
                self.glancesgrabhddtemp = glancesGrabHDDTemp()
            except Exception:
                self.hddtemp_tag = False

1332
        if batinfo_lib_tag:
A
Alessio Sergi 已提交
1333
            self.glancesgrabbat = glancesGrabBat()
1334

1335
        # Init the process list
A
asergi 已提交
1336
        self.process_list_refresh = True
1337
        self.process_list_sortedby = ''
1338
        self.glancesgrabprocesses = GlancesGrabProcesses()
1339 1340 1341 1342 1343

    def _init_host(self):
        self.host = {}
        self.host['os_name'] = platform.system()
        self.host['hostname'] = platform.node()
1344 1345 1346 1347 1348 1349
        # More precise but not user friendly
        #~ if platform.uname()[4]:
            #~ self.host['platform'] = platform.uname()[4]
        #~ else:
            #~ self.host['platform'] = platform.architecture()[0]
        # This one is better
1350 1351 1352 1353 1354
        self.host['platform'] = platform.architecture()[0]
        is_archlinux = os.path.exists(os.path.join("/", "etc", "arch-release"))
        if self.host['os_name'] == "Linux":
            if is_archlinux:
                self.host['linux_distro'] = "Arch Linux"
A
asergi 已提交
1355
            else:
1356
                linux_distro = platform.linux_distribution()
1357
                self.host['linux_distro'] = ' '.join(linux_distro[:2])
1358 1359 1360 1361 1362 1363 1364
            self.host['os_version'] = platform.release()
        elif self.host['os_name'] == "FreeBSD":
            self.host['os_version'] = platform.release()
        elif self.host['os_name'] == "Darwin":
            self.host['os_version'] = platform.mac_ver()[0]
        elif self.host['os_name'] == "Windows":
            os_version = platform.win32_ver()
1365
            self.host['os_version'] = ' '.join(os_version[::2])
1366 1367
        else:
            self.host['os_version'] = ""
1368

1369
    def __update__(self, input_stats):
N
Nicolas Hennion 已提交
1370 1371 1372
        """
        Update the stats
        """
A
asergi 已提交
1373
        # CPU
A
asergi 已提交
1374
        cputime = psutil.cpu_times(percpu=False)
A
asergi 已提交
1375
        cputime_total = cputime.user + cputime.system + cputime.idle
1376
        # Only available on some OS
A
asergi 已提交
1377 1378 1379 1380 1381 1382 1383 1384
        if hasattr(cputime, 'nice'):
            cputime_total += cputime.nice
        if hasattr(cputime, 'iowait'):
            cputime_total += cputime.iowait
        if hasattr(cputime, 'irq'):
            cputime_total += cputime.irq
        if hasattr(cputime, 'softirq'):
            cputime_total += cputime.softirq
1385
        if not hasattr(self, 'cputime_old'):
1386 1387
            self.cputime_old = cputime
            self.cputime_total_old = cputime_total
1388
            self.cpu = {}
A
asergi 已提交
1389
        else:
1390 1391
            self.cputime_new = cputime
            self.cputime_total_new = cputime_total
1392 1393 1394
            try:
                percent = 100 / (self.cputime_total_new -
                                 self.cputime_total_old)
A
asergi 已提交
1395 1396 1397 1398 1399 1400
                self.cpu = {'user': (self.cputime_new.user -
                                     self.cputime_old.user) * percent,
                            'system': (self.cputime_new.system -
                                       self.cputime_old.system) * percent,
                            'idle': (self.cputime_new.idle -
                                     self.cputime_old.idle) * percent}
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
                if hasattr(self.cputime_new, 'nice'):
                    self.cpu['nice'] = (self.cputime_new.nice -
                                        self.cputime_old.nice) * percent
                if hasattr(self.cputime_new, 'iowait'):
                    self.cpu['iowait'] = (self.cputime_new.iowait -
                                          self.cputime_old.iowait) * percent
                if hasattr(self.cputime_new, 'irq'):
                    self.cpu['irq'] = (self.cputime_new.irq -
                                       self.cputime_old.irq) * percent
                self.cputime_old = self.cputime_new
                self.cputime_total_old = self.cputime_total_new
            except Exception:
A
asergi 已提交
1413 1414
                self.cpu = {}

A
asergi 已提交
1415
        # Per-CPU
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
        percputime = psutil.cpu_times(percpu=True)
        percputime_total = []
        for i in range(len(percputime)):
            percputime_total.append(percputime[i].user +
                                    percputime[i].system +
                                    percputime[i].idle)
        # Only available on some OS
        for i in range(len(percputime)):
            if hasattr(percputime[i], 'nice'):
                percputime_total[i] += percputime[i].nice
        for i in range(len(percputime)):
            if hasattr(percputime[i], 'iowait'):
                percputime_total[i] += percputime[i].iowait
        for i in range(len(percputime)):
            if hasattr(percputime[i], 'irq'):
                percputime_total[i] += percputime[i].irq
        for i in range(len(percputime)):
            if hasattr(percputime[i], 'softirq'):
                percputime_total[i] += percputime[i].softirq
1435
        if not hasattr(self, 'percputime_old'):
1436 1437
            self.percputime_old = percputime
            self.percputime_total_old = percputime_total
1438
            self.percpu = []
N
Nicolas Hennion 已提交
1439
        else:
1440 1441 1442 1443
            self.percputime_new = percputime
            self.percputime_total_new = percputime_total
            perpercent = []
            self.percpu = []
1444 1445 1446 1447
            try:
                for i in range(len(self.percputime_new)):
                    perpercent.append(100 / (self.percputime_total_new[i] -
                                             self.percputime_total_old[i]))
A
asergi 已提交
1448 1449 1450 1451 1452 1453
                    cpu = {'user': (self.percputime_new[i].user -
                                    self.percputime_old[i].user) * perpercent[i],
                           'system': (self.percputime_new[i].system -
                                      self.percputime_old[i].system) * perpercent[i],
                           'idle': (self.percputime_new[i].idle -
                                    self.percputime_old[i].idle) * perpercent[i]}
1454 1455 1456
                    if hasattr(self.percputime_new[i], 'nice'):
                        cpu['nice'] = (self.percputime_new[i].nice -
                                       self.percputime_old[i].nice) * perpercent[i]
1457 1458 1459 1460 1461 1462 1463 1464 1465
                    if hasattr(self.percputime_new[i], 'iowait'):
                        cpu['iowait'] = (self.percputime_new[i].iowait -
                                         self.percputime_old[i].iowait) * perpercent[i]
                    if hasattr(self.percputime_new[i], 'irq'):
                        cpu['irq'] = (self.percputime_new[i].irq -
                                      self.percputime_old[i].irq) * perpercent[i]
                    if hasattr(self.percputime_new[i], 'softirq'):
                        cpu['softirq'] = (self.percputime_new[i].softirq -
                                          self.percputime_old[i].softirq) * perpercent[i]
1466 1467 1468 1469
                    self.percpu.append(cpu)
                self.percputime_old = self.percputime_new
                self.percputime_total_old = self.percputime_total_new
            except Exception:
N
Nicolas Hennion 已提交
1470 1471
                self.percpu = []

A
asergi 已提交
1472
        # LOAD
1473 1474 1475 1476 1477
        if hasattr(os, 'getloadavg'):
            getload = os.getloadavg()
            self.load = {'min1': getload[0],
                         'min5': getload[1],
                         'min15': getload[2]}
1478
        else:
1479
            self.load = {}
A
asergi 已提交
1480 1481

        # MEM
A
asergi 已提交
1482
        # psutil >= 0.6
1483
        if psutil_mem_vm:
A
asergi 已提交
1484
            # RAM
1485
            phymem = psutil.virtual_memory()
A
asergi 已提交
1486 1487

            # buffers and cached (Linux, BSD)
N
Nicolas Hennion 已提交
1488 1489
            buffers = getattr(phymem, 'buffers', 0)
            cached = getattr(phymem, 'cached', 0)
A
asergi 已提交
1490

A
asergi 已提交
1491
            # active and inactive not available on Windows
N
Nicolas Hennion 已提交
1492 1493
            active = getattr(phymem, 'active', 0)
            inactive = getattr(phymem, 'inactive', 0)
A
asergi 已提交
1494

A
asergi 已提交
1495 1496 1497 1498 1499 1500 1501 1502 1503
            # phymem free and usage
            total = phymem.total
            free = phymem.available  # phymem.free + buffers + cached
            used = total - free

            self.mem = {'total': total,
                        'percent': phymem.percent,
                        'used': used,
                        'free': free,
A
asergi 已提交
1504 1505
                        'active': active,
                        'inactive': inactive,
A
asergi 已提交
1506 1507 1508 1509
                        'buffers': buffers,
                        'cached': cached}

            # Swap
1510 1511 1512
            # try... is an hack for issue #152
            try:
                virtmem = psutil.swap_memory()
A
asergi 已提交
1513
            except Exception:
1514 1515 1516 1517 1518 1519
                self.memswap = {}
            else:
                self.memswap = {'total': virtmem.total,
                                'used': virtmem.used,
                                'free': virtmem.free,
                                'percent': virtmem.percent}
1520
        else:
A
asergi 已提交
1521 1522
            # psutil < 0.6
            # RAM
1523 1524
            if hasattr(psutil, 'phymem_usage'):
                phymem = psutil.phymem_usage()
A
asergi 已提交
1525 1526

                # buffers and cached (Linux, BSD)
A
asergi 已提交
1527 1528
                buffers = getattr(psutil, 'phymem_buffers', 0)()
                cached = getattr(psutil, 'cached_phymem', 0)()
A
asergi 已提交
1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541

                # phymem free and usage
                total = phymem.total
                free = phymem.free + buffers + cached
                used = total - free

                # active and inactive not available for psutil < 0.6
                self.mem = {'total': total,
                            'percent': phymem.percent,
                            'used': used,
                            'free': free,
                            'buffers': buffers,
                            'cached': cached}
1542 1543
            else:
                self.mem = {}
A
asergi 已提交
1544 1545

            # Swap
1546 1547
            if hasattr(psutil, 'virtmem_usage'):
                virtmem = psutil.virtmem_usage()
1548 1549 1550
                self.memswap = {'total': virtmem.total,
                                'used': virtmem.used,
                                'free': virtmem.free,
1551
                                'percent': virtmem.percent}
1552
            else:
1553
                self.memswap = {}
A
asergi 已提交
1554 1555

        # NET
1556
        if network_tag and not self.network_error_tag:
1557
            self.network = []
A
Alessio Sergi 已提交
1558

1559 1560 1561
            # By storing time data we enable Rx/s and Tx/s calculations in the
            # XML/RPC API, which would otherwise be overly difficult work
            # for users of the API
1562
            time_since_update = getTimeSinceLastUpdate('net')
A
Alessio Sergi 已提交
1563 1564 1565

            if psutil_net_io_counters:
                # psutil >= 1.0.0
1566
                try:
F
fraoustin 已提交
1567
                    get_net_io_counters = psutil.net_io_counters(pernic=True)
1568
                except IOError:
1569
                    self.network_error_tag = True
A
Alessio Sergi 已提交
1570 1571
            else:
                # psutil < 1.0.0
1572
                try:
F
fraoustin 已提交
1573
                    get_net_io_counters = psutil.network_io_counters(pernic=True)
1574
                except IOError:
1575
                    self.network_error_tag = True
A
Alessio Sergi 已提交
1576

A
asergi 已提交
1577
            if not hasattr(self, 'network_old'):
1578
                try:
A
Alessio Sergi 已提交
1579
                    self.network_old = get_net_io_counters
1580
                except (IOError, UnboundLocalError):
F
fraoustin 已提交
1581
                    self.network_error_tag = True
A
asergi 已提交
1582
            else:
A
Alessio Sergi 已提交
1583
                self.network_new = get_net_io_counters
A
asergi 已提交
1584 1585 1586 1587
                for net in self.network_new:
                    try:
                        # Try necessary to manage dynamic network interface
                        netstat = {}
1588
                        netstat['time_since_update'] = time_since_update
A
asergi 已提交
1589
                        netstat['interface_name'] = net
1590
                        netstat['cumulative_rx'] = self.network_new[net].bytes_recv
A
asergi 已提交
1591 1592
                        netstat['rx'] = (self.network_new[net].bytes_recv -
                                         self.network_old[net].bytes_recv)
1593
                        netstat['cumulative_tx'] = self.network_new[net].bytes_sent
A
asergi 已提交
1594 1595
                        netstat['tx'] = (self.network_new[net].bytes_sent -
                                         self.network_old[net].bytes_sent)
1596 1597
                        netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
                                                    netstat['cumulative_tx'])
1598
                        netstat['cx'] = netstat['rx'] + netstat['tx']
A
asergi 已提交
1599 1600 1601 1602 1603
                    except Exception:
                        continue
                    else:
                        self.network.append(netstat)
                self.network_old = self.network_new
A
asergi 已提交
1604

1605
        # SENSORS
A
asergi 已提交
1606
        if sensors_tag:
1607
            self.sensors = self.glancesgrabsensors.get()
1608

M
MendelGusmao 已提交
1609 1610 1611 1612
        # HDDTEMP
        if hddtemp_tag:
            self.hddtemp = self.glancesgrabhddtemp.get()

1613 1614
        # BATERRIES INFORMATION
        if batinfo_lib_tag:
A
Alessio Sergi 已提交
1615
            self.batpercent = self.glancesgrabbat.getcapacitypercent()
1616

A
asergi 已提交
1617
        # DISK I/O
1618
        if diskio_tag and not self.diskio_error_tag:
1619
            time_since_update = getTimeSinceLastUpdate('disk')
1620
            self.diskio = []
A
asergi 已提交
1621
            if not hasattr(self, 'diskio_old'):
1622 1623 1624 1625
                try:
                    self.diskio_old = psutil.disk_io_counters(perdisk=True)
                except IOError:
                    self.diskio_error_tag = True
A
asergi 已提交
1626 1627 1628 1629 1630 1631
            else:
                self.diskio_new = psutil.disk_io_counters(perdisk=True)
                for disk in self.diskio_new:
                    try:
                        # Try necessary to manage dynamic disk creation/del
                        diskstat = {}
1632
                        diskstat['time_since_update'] = time_since_update
A
asergi 已提交
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644
                        diskstat['disk_name'] = disk
                        diskstat['read_bytes'] = (
                            self.diskio_new[disk].read_bytes -
                            self.diskio_old[disk].read_bytes)
                        diskstat['write_bytes'] = (
                            self.diskio_new[disk].write_bytes -
                            self.diskio_old[disk].write_bytes)
                    except Exception:
                        continue
                    else:
                        self.diskio.append(diskstat)
                self.diskio_old = self.diskio_new
A
asergi 已提交
1645 1646

        # FILE SYSTEM
A
asergi 已提交
1647
        if fs_tag:
1648
            self.fs = self.glancesgrabfs.get()
A
asergi 已提交
1649 1650

        # PROCESS
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
        if process_tag:
            self.glancesgrabprocesses.update()
            processcount = self.glancesgrabprocesses.getcount()
            process = self.glancesgrabprocesses.getlist()
            if not hasattr(self, 'process'):
                self.processcount = {}
                self.process = []
            else:
                self.processcount = processcount
                self.process = process
1661

A
asergi 已提交
1662 1663 1664 1665
        # Get the current date/time
        self.now = datetime.now()

        # Get the number of core (CPU) (Used to display load alerts)
1666
        self.core_number = psutil.NUM_CPUS
A
asergi 已提交
1667

1668 1669 1670
        # get psutil version
        self.psutil_version = psutil.__version__

1671 1672 1673
    def update(self, input_stats={}):
        # Update the stats
        self.__update__(input_stats)
1674

1675 1676 1677 1678 1679
    def getSortedBy(self):
        return self.process_list_sortedby

    def getAll(self):
        return self.all_stats
A
asergi 已提交
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689

    def getHost(self):
        return self.host

    def getSystem(self):
        return self.host

    def getCpu(self):
        return self.cpu

N
Nicolas Hennion 已提交
1690 1691 1692
    def getPerCpu(self):
        return self.percpu

A
asergi 已提交
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705
    def getCore(self):
        return self.core_number

    def getLoad(self):
        return self.load

    def getMem(self):
        return self.mem

    def getMemSwap(self):
        return self.memswap

    def getNetwork(self):
A
asergi 已提交
1706
        if network_tag:
1707
            return sorted(self.network, key=lambda network: network['interface_name'])
A
asergi 已提交
1708
        else:
N
Nicolas Hennion 已提交
1709 1710 1711 1712
            return []

    def getSensors(self):
        if sensors_tag:
1713
            return sorted(self.sensors, key=lambda sensors: sensors['label'])
N
Nicolas Hennion 已提交
1714 1715
        else:
            return []
A
asergi 已提交
1716

M
MendelGusmao 已提交
1717 1718
    def getHDDTemp(self):
        if hddtemp_tag:
1719
            return sorted(self.hddtemp, key=lambda hddtemp: hddtemp['label'])
M
MendelGusmao 已提交
1720 1721 1722
        else:
            return []

1723
    def getBatPercent(self):
A
Alessio Sergi 已提交
1724 1725 1726 1727
        if batinfo_lib_tag:
            return self.batpercent
        else:
            return []
1728

A
asergi 已提交
1729
    def getDiskIO(self):
A
asergi 已提交
1730
        if diskio_tag:
A
asergi 已提交
1731 1732
            return sorted(self.diskio, key=lambda diskio: diskio['disk_name'])
        else:
N
Nicolas Hennion 已提交
1733
            return []
A
asergi 已提交
1734 1735

    def getFs(self):
A
asergi 已提交
1736
        if fs_tag:
A
asergi 已提交
1737 1738
            return sorted(self.fs, key=lambda fs: fs['mnt_point'])
        else:
N
Nicolas Hennion 已提交
1739
            return []
A
asergi 已提交
1740 1741

    def getProcessCount(self):
1742 1743 1744 1745
        if process_tag:
            return self.processcount
        else:
            return 0
A
asergi 已提交
1746 1747 1748 1749 1750

    def getProcessList(self, sortedby='auto'):
        """
        Return the sorted process list
        """
1751 1752
        if not process_tag:
            return []
A
asergi 已提交
1753
        if self.process == {} or 'limits' not in globals():
N
Nicolas Hennion 已提交
1754 1755
            return self.process

A
asergi 已提交
1756 1757
        sortedReverse = True
        if sortedby == 'auto':
A
asergi 已提交
1758 1759
            # Auto selection (default: sort by CPU%)
            sortedby = 'cpu_percent'
1760
            # Dynamic choice
A
asergi 已提交
1761 1762
            if ('iowait' in self.cpu and
                self.cpu['iowait'] > limits.getCPUWarning(stat='iowait')):
1763 1764
                # If CPU IOWait > 70% sort by IORATE usage
                sortedby = 'io_counters'
A
asergi 已提交
1765 1766
            elif (self.mem['total'] != 0 and
                  self.mem['used'] * 100 / self.mem['total'] > limits.getMEMWarning()):
1767 1768
                # If global MEM > 70% sort by MEM usage
                sortedby = 'memory_percent'
1769
        elif sortedby == 'name':
A
asergi 已提交
1770 1771
            sortedReverse = False

A
asergi 已提交
1772
        if sortedby == 'io_counters':
1773 1774 1775 1776 1777 1778
            try:
                # Sort process by IO rate (sum IO read + IO write)
                listsorted = sorted(self.process,
                                    key=lambda process: process[sortedby][0] -
                                    process[sortedby][2] + process[sortedby][1] -
                                    process[sortedby][3], reverse=sortedReverse)
1779
            except Exception:
1780
                listsorted = sorted(self.process, key=lambda process: process['cpu_percent'],
N
Nicolas Hennion 已提交
1781
                                    reverse=sortedReverse)
1782 1783
        else:
            # Others sorts
A
asergi 已提交
1784 1785
            listsorted = sorted(self.process, key=lambda process: process[sortedby],
                                reverse=sortedReverse)
1786 1787 1788 1789 1790 1791

        # Save the latest sort type in a global var
        self.process_list_sortedby = sortedby

        # Return the sorted list
        return listsorted
A
asergi 已提交
1792

1793 1794 1795
    def getPsutilVersion(self):
        return self.psutil_version

A
asergi 已提交
1796 1797 1798 1799
    def getNow(self):
        return self.now


1800
class GlancesStatsServer(GlancesStats):
1801

1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820
    def __init__(self):
        GlancesStats.__init__(self)

        # Init the all_stats used by the server
        # all_stats is a dict of dicts filled by the server
        self.all_stats = collections.defaultdict(dict)
        self._init_host()
        self.all_stats["host"] = self.host

    def __update__(self, input_stats):
        """
        Update the stats
        """
        GlancesStats.__update__(self, input_stats)

        self.all_stats["cpu"] = self.cpu
        self.all_stats["percpu"] = self.percpu
        self.all_stats["load"] = self.load
        self.all_stats["mem"] = self.mem
A
asergi 已提交
1821
        self.all_stats["memswap"] = self.memswap
A
asergi 已提交
1822
        self.all_stats["network"] = self.network if network_tag else []
1823
        self.all_stats["sensors"] = self.sensors if sensors_tag else []
M
MendelGusmao 已提交
1824
        self.all_stats["hddtemp"] = self.hddtemp if hddtemp_tag else []
1825
        self.all_stats["batpercent"] = self.batpercent if batinfo_lib_tag else []
A
asergi 已提交
1826 1827
        self.all_stats["diskio"] = self.diskio if diskio_tag else []
        self.all_stats["fs"] = self.fs if fs_tag else []
1828 1829
        self.all_stats["processcount"] = self.processcount if process_tag else 0
        self.all_stats["process"] = self.process if process_tag else []
1830
        self.all_stats["core_number"] = self.core_number
1831
        self.all_stats["psutil_version"] = self.psutil_version
1832 1833 1834 1835

        # Get the current date/time
        self.now = datetime.now()

1836 1837 1838
    def getAll(self):
        return self.all_stats

1839 1840

class GlancesStatsClient(GlancesStats):
1841

1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
    def __init__(self):
        GlancesStats.__init__(self)

    def __update__(self, input_stats):
        """
        Update the stats
        """
        if input_stats != {}:
            self.host = input_stats["host"]
            self.cpu = input_stats["cpu"]
            self.percpu = input_stats["percpu"]
            self.load = input_stats["load"]
            self.mem = input_stats["mem"]
            self.memswap = input_stats["memswap"]
N
Nicolas Hennion 已提交
1856 1857
            try:
                self.network = input_stats["network"]
1858
            except Exception:
N
Nicolas Hennion 已提交
1859
                self.network = []
1860 1861
            try:
                self.sensors = input_stats["sensors"]
1862
            except Exception:
N
Nicolas Hennion 已提交
1863
                self.sensors = []
M
MendelGusmao 已提交
1864 1865
            try:
                self.hddtemp = input_stats["hddtemp"]
1866
            except Exception:
A
Alessio Sergi 已提交
1867
                self.hddtemp = []
1868 1869
            try:
                self.batpercent = input_stats["batpercent"]
1870
            except Exception:
1871
                self.batpercent = []
N
Nicolas Hennion 已提交
1872 1873
            try:
                self.diskio = input_stats["diskio"]
1874
            except Exception:
N
Nicolas Hennion 已提交
1875 1876 1877
                self.diskio = []
            try:
                self.fs = input_stats["fs"]
1878
            except Exception:
N
Nicolas Hennion 已提交
1879
                self.fs = []
1880 1881 1882
            self.processcount = input_stats["processcount"]
            self.process = input_stats["process"]
            self.core_number = input_stats["core_number"]
1883
            self.psutil_version = input_stats["psutil_version"]
1884 1885 1886 1887 1888

        # Get the current date/time
        self.now = datetime.now()


A
asergi 已提交
1889 1890 1891 1892 1893 1894 1895 1896 1897
class glancesScreen:
    """
    This class manage the screen (display and key pressed)
    """
    # By default the process list is automatically sorted
    # If global CPU > WANRING => Sorted by CPU usage
    # If global used MEM > WARINING => Sorted by MEM usage
    __process_sortedby = 'auto'

1898
    def __init__(self, refresh_time=1, use_bold=1):
A
asergi 已提交
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908
        # Global information to display
        self.__version = __version__

        # Init windows positions
        self.term_w = 80
        self.term_h = 24
        self.system_x = 0
        self.system_y = 0
        self.cpu_x = 0
        self.cpu_y = 2
A
asergi 已提交
1909
        self.load_x = 17
A
asergi 已提交
1910
        self.load_y = 2
A
asergi 已提交
1911
        self.mem_x = 33
A
asergi 已提交
1912 1913 1914
        self.mem_y = 2
        self.network_x = 0
        self.network_y = 7
1915 1916
        self.sensors_x = 0
        self.sensors_y = -1
M
MendelGusmao 已提交
1917
        self.hddtemp_x = 0
A
Alessio Sergi 已提交
1918
        self.hddtemp_y = -1
A
asergi 已提交
1919 1920 1921 1922
        self.diskio_x = 0
        self.diskio_y = -1
        self.fs_x = 0
        self.fs_y = -1
A
asergi 已提交
1923
        self.process_x = 26
A
asergi 已提交
1924 1925 1926 1927 1928 1929 1930
        self.process_y = 7
        self.log_x = 0
        self.log_y = -1
        self.help_x = 0
        self.help_y = 0
        self.now_x = 79
        self.now_y = 3
1931 1932
        self.bat_x = 0
        self.bat_y = 3
A
asergi 已提交
1933 1934 1935 1936
        self.caption_x = 0
        self.caption_y = 3

        # Init the curses screen
1937
        self.screen = curses.initscr()
A
asergi 已提交
1938
        if not self.screen:
1939
            print(_("Error: Cannot init the curses library.\n"))
A
asergi 已提交
1940

N
Nicolas Hennion 已提交
1941 1942 1943
        # Set curses options
        if hasattr(curses, 'start_color'):
            curses.start_color()
A
asergi 已提交
1944
        if hasattr(curses, 'use_default_colors'):
N
Nicolas Hennion 已提交
1945
            curses.use_default_colors()
A
asergi 已提交
1946
        if hasattr(curses, 'noecho'):
N
Nicolas Hennion 已提交
1947
            curses.noecho()
A
asergi 已提交
1948
        if hasattr(curses, 'cbreak'):
N
Nicolas Hennion 已提交
1949
            curses.cbreak()
A
asergi 已提交
1950
        if hasattr(curses, 'curs_set'):
N
Nicolas Hennion 已提交
1951 1952
            try:
                curses.curs_set(0)
1953
            except Exception:
N
Nicolas Hennion 已提交
1954
                pass
A
asergi 已提交
1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972

        # Init colors
        self.hascolors = False
        if curses.has_colors() and curses.COLOR_PAIRS > 8:
            self.hascolors = True
            # FG color, BG color
            curses.init_pair(1, curses.COLOR_WHITE, -1)
            curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_RED)
            curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_GREEN)
            curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLUE)
            curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
            curses.init_pair(6, curses.COLOR_RED, -1)
            curses.init_pair(7, curses.COLOR_GREEN, -1)
            curses.init_pair(8, curses.COLOR_BLUE, -1)
            curses.init_pair(9, curses.COLOR_MAGENTA, -1)
        else:
            self.hascolors = False

1973 1974 1975 1976 1977 1978 1979 1980
        if use_bold:
            A_BOLD = curses.A_BOLD
        else:
            A_BOLD = 0

        self.title_color = A_BOLD
        self.title_underline_color = A_BOLD | curses.A_UNDERLINE
        self.help_color = A_BOLD
A
asergi 已提交
1981 1982 1983
        if self.hascolors:
            # Colors text styles
            self.no_color = curses.color_pair(1)
1984 1985 1986 1987 1988 1989 1990 1991
            self.default_color = curses.color_pair(3) | A_BOLD
            self.ifCAREFUL_color = curses.color_pair(4) | A_BOLD
            self.ifWARNING_color = curses.color_pair(5) | A_BOLD
            self.ifCRITICAL_color = curses.color_pair(2) | A_BOLD
            self.default_color2 = curses.color_pair(7) | A_BOLD
            self.ifCAREFUL_color2 = curses.color_pair(8) | A_BOLD
            self.ifWARNING_color2 = curses.color_pair(9) | A_BOLD
            self.ifCRITICAL_color2 = curses.color_pair(6) | A_BOLD
A
asergi 已提交
1992 1993 1994 1995 1996
        else:
            # B&W text styles
            self.no_color = curses.A_NORMAL
            self.default_color = curses.A_NORMAL
            self.ifCAREFUL_color = curses.A_UNDERLINE
1997
            self.ifWARNING_color = A_BOLD
A
asergi 已提交
1998 1999 2000
            self.ifCRITICAL_color = curses.A_REVERSE
            self.default_color2 = curses.A_NORMAL
            self.ifCAREFUL_color2 = curses.A_UNDERLINE
2001
            self.ifWARNING_color2 = A_BOLD
A
asergi 已提交
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
            self.ifCRITICAL_color2 = curses.A_REVERSE

        # Define the colors list (hash table) for logged stats
        self.__colors_list = {
            'DEFAULT': self.no_color,
            'OK': self.default_color,
            'CAREFUL': self.ifCAREFUL_color,
            'WARNING': self.ifWARNING_color,
            'CRITICAL': self.ifCRITICAL_color
        }

        # Define the colors list (hash table) for non logged stats
        self.__colors_list2 = {
            'DEFAULT': self.no_color,
            'OK': self.default_color2,
            'CAREFUL': self.ifCAREFUL_color2,
            'WARNING': self.ifWARNING_color2,
            'CRITICAL': self.ifCRITICAL_color2
        }

        # What are we going to display
A
asergi 已提交
2023
        self.network_tag = network_tag
2024
        self.sensors_tag = sensors_tag
M
MendelGusmao 已提交
2025
        self.hddtemp_tag = hddtemp_tag
A
asergi 已提交
2026 2027
        self.diskio_tag = diskio_tag
        self.fs_tag = fs_tag
A
asergi 已提交
2028 2029
        self.log_tag = True
        self.help_tag = False
2030
        self.percpu_tag = percpu_tag
2031
        self.process_tag = process_tag
2032
        self.net_byteps_tag = network_bytepersec_tag
2033 2034
        self.network_stats_combined = False
        self.network_stats_cumulative = False
A
asergi 已提交
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053

        # Init main window
        self.term_window = self.screen.subwin(0, 0)

        # Init refresh time
        self.__refresh_time = refresh_time

        # Catch key pressed with non blocking mode
        self.term_window.keypad(1)
        self.term_window.nodelay(1)
        self.pressedkey = -1

    def setProcessSortedBy(self, sorted):
        self.__process_sortedautoflag = False
        self.__process_sortedby = sorted

    def getProcessSortedBy(self):
        return self.__process_sortedby

2054
    def __autoUnit(self, val, low_precision=False):
A
asergi 已提交
2055
        """
J
Jon Renner 已提交
2056 2057 2058 2059
        Make a nice human readable string out of val
        Number of decimal places increases as quantity approaches 1

        examples:
2060 2061 2062 2063 2064 2065 2066
        CASE: 613421788        RESULT:       585M low_precision:       585M
        CASE: 5307033647       RESULT:      4.94G low_precision:       4.9G
        CASE: 44968414685      RESULT:      41.9G low_precision:      41.9G
        CASE: 838471403472     RESULT:       781G low_precision:       781G
        CASE: 9683209690677    RESULT:      8.81T low_precision:       8.8T
        CASE: 1073741824       RESULT:      1024M low_precision:      1024M
        CASE: 1181116006       RESULT:      1.10G low_precision:       1.1G
J
Jon Renner 已提交
2067

2068 2069
        parameter 'low_precision=True' returns less decimal places.
        potentially sacrificing precision for more readability
A
asergi 已提交
2070 2071 2072
        """
        symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
        prefix = {
2073 2074 2075 2076 2077
            'Y': 1208925819614629174706176,
            'Z': 1180591620717411303424,
            'E': 1152921504606846976,
            'P': 1125899906842624,
            'T': 1099511627776,
A
asergi 已提交
2078 2079 2080 2081 2082 2083
            'G': 1073741824,
            'M': 1048576,
            'K': 1024
        }

        for key in reversed(symbols):
J
Jon Renner 已提交
2084 2085 2086 2087 2088
            value = float(val) / prefix[key]
            if value > 1:
                fixed_decimal_places = 0
                if value < 10:
                    fixed_decimal_places = 2
J
Jon Renner 已提交
2089 2090
                elif value < 100:
                    fixed_decimal_places = 1
2091 2092 2093 2094 2095 2096 2097
                if low_precision:
                    if key in 'MK':
                        fixed_decimal_places = 0
                    else:
                        fixed_decimal_places = min(1, fixed_decimal_places)
                elif key in 'K':
                    fixed_decimal_places = 0
J
Jon Renner 已提交
2098 2099
                formatter = "{0:.%df}{1}" % fixed_decimal_places
                return formatter.format(value, key)
2100
        return "{0!s}".format(val)
A
asergi 已提交
2101

A
asergi 已提交
2102
    def __getCpuAlert(self, current=0, max=100, stat=''):
2103 2104 2105 2106
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
N
Nicolas Hennion 已提交
2107
        # stat is USER, SYSTEM or IOWAIT
2108 2109 2110 2111
        try:
            variable = (current * 100) / max
        except ZeroDivisionError:
            return 'DEFAULT'
A
asergi 已提交
2112

A
asergi 已提交
2113
        if variable > limits.getCPUCritical(stat=stat):
2114
            return 'CRITICAL'
A
asergi 已提交
2115
        elif variable > limits.getCPUWarning(stat=stat):
2116
            return 'WARNING'
A
asergi 已提交
2117
        elif variable > limits.getCPUCareful(stat=stat):
2118
            return 'CAREFUL'
A
asergi 已提交
2119

2120 2121
        return 'OK'

A
asergi 已提交
2122
    def __getCpuColor(self, current=0, max=100, stat=''):
2123 2124
        return self.__colors_list[self.__getCpuAlert(current, max, stat)]

A
asergi 已提交
2125
    def __getCpuColor2(self, current=0, max=100, stat=''):
2126
        return self.__colors_list2[self.__getCpuAlert(current, max, stat)]
N
Nicolas Hennion 已提交
2127

A
asergi 已提交
2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145
    def __getLoadAlert(self, current=0, core=1):
        # If current < CAREFUL*core of max then alert = OK
        # If current > CAREFUL*core of max then alert = CAREFUL
        # If current > WARNING*core of max then alert = WARNING
        # If current > CRITICAL*core of max then alert = CRITICAL

        if current > limits.getLOADCritical(core):
            return 'CRITICAL'
        elif current > limits.getLOADWarning(core):
            return 'WARNING'
        elif current > limits.getLOADCareful(core):
            return 'CAREFUL'

        return 'OK'

    def __getLoadColor(self, current=0, core=1):
        return self.__colors_list[self.__getLoadAlert(current, core)]

N
Nicolas Hennion 已提交
2146 2147 2148
    def __getLoadColor2(self, current=0, core=1):
        return self.__colors_list2[self.__getLoadAlert(current, core)]

A
asergi 已提交
2149
    def __getMemAlert(self, current=0, max=100):
2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
        try:
            variable = (current * 100) / max
        except ZeroDivisionError:
            return 'DEFAULT'

        if variable > limits.getMEMCritical():
            return 'CRITICAL'
        elif variable > limits.getMEMWarning():
            return 'WARNING'
        elif variable > limits.getMEMCareful():
            return 'CAREFUL'

        return 'OK'

N
Nicolas Hennion 已提交
2168 2169 2170 2171 2172 2173 2174
    def __getMemColor(self, current=0, max=100):
        return self.__colors_list[self.__getMemAlert(current, max)]

    def __getMemColor2(self, current=0, max=100):
        return self.__colors_list2[self.__getMemAlert(current, max)]

    def __getSwapAlert(self, current=0, max=100):
2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
        try:
            variable = (current * 100) / max
        except ZeroDivisionError:
            return 'DEFAULT'

        if variable > limits.getSWAPCritical():
            return 'CRITICAL'
        elif variable > limits.getSWAPWarning():
            return 'WARNING'
        elif variable > limits.getSWAPCareful():
            return 'CAREFUL'

        return 'OK'
A
asergi 已提交
2192

N
Nicolas Hennion 已提交
2193 2194
    def __getSwapColor(self, current=0, max=100):
        return self.__colors_list[self.__getSwapAlert(current, max)]
A
asergi 已提交
2195

N
Nicolas Hennion 已提交
2196 2197
    def __getSwapColor2(self, current=0, max=100):
        return self.__colors_list2[self.__getSwapAlert(current, max)]
2198

2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217
    def __getFsAlert(self, current=0, max=100):
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
        try:
            variable = (current * 100) / max
        except ZeroDivisionError:
            return 'DEFAULT'

        if variable > limits.getSWAPCritical():
            return 'CRITICAL'
        elif variable > limits.getSWAPWarning():
            return 'WARNING'
        elif variable > limits.getSWAPCareful():
            return 'CAREFUL'

        return 'OK'

A
asergi 已提交
2218
    def __getFsColor(self, current=0, max=100):
N
Nicolas Hennion 已提交
2219
        return self.__colors_list[self.__getFsAlert(current, max)]
A
asergi 已提交
2220

N
Nicolas Hennion 已提交
2221 2222
    def __getFsColor2(self, current=0, max=100):
        return self.__colors_list2[self.__getFsAlert(current, max)]
A
asergi 已提交
2223

N
Nicolas Hennion 已提交
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
    def __getSensorsAlert(self, current=0):
        # Alert for Sensors (temperature in degre)
        # If current < CAREFUL then alert = OK
        # If current > CAREFUL then alert = CAREFUL
        # If current > WARNING then alert = WARNING
        # If current > CRITICALthen alert = CRITICAL

        if current > limits.getTEMPCritical():
            return 'CRITICAL'
        elif current > limits.getTEMPWarning():
            return 'WARNING'
        elif current > limits.getTEMPCareful():
            return 'CAREFUL'

        return 'OK'

    def __getSensorsColor(self, current=0):
        """
N
Nicolas Hennion 已提交
2242 2243 2244 2245 2246 2247 2248
        Return color for Sensors temperature
        """
        return self.__colors_list[self.__getSensorsAlert(current)]

    def __getSensorsColor2(self, current=0):
        """
        Return color for Sensors temperature
N
Nicolas Hennion 已提交
2249 2250 2251
        """
        return self.__colors_list2[self.__getSensorsAlert(current)]

2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278
    def __getHDDTempAlert(self, current=0):
        # Alert for HDDTemp (temperature in degre)
        # If current < CAREFUL then alert = OK
        # If current > CAREFUL then alert = CAREFUL
        # If current > WARNING then alert = WARNING
        # If current > CRITICALthen alert = CRITICAL
        if current > limits.getHDDTEMPCritical():
            return 'CRITICAL'
        elif current > limits.getHDDTEMPWarning():
            return 'WARNING'
        elif current > limits.getHDDTEMPCareful():
            return 'CAREFUL'

        return 'OK'

    def __getHDDTempColor(self, current=0):
        """
        Return color for HDDTemp temperature
        """
        return self.__colors_list[self.__getHDDTempAlert(current)]

    def __getHDDTempColor2(self, current=0):
        """
        Return color for HDDTemp temperature
        """
        return self.__colors_list2[self.__getHDDTempAlert(current)]

2279
    def __getProcessAlert(self, current=0, max=100, stat='', core=1):
N
Nicolas Hennion 已提交
2280 2281 2282 2283
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
2284
        # If stat == 'CPU', get core into account...
N
Nicolas Hennion 已提交
2285 2286 2287 2288 2289
        try:
            variable = (current * 100) / max
        except ZeroDivisionError:
            return 'DEFAULT'

2290
        if variable > limits.getProcessCritical(stat=stat, core=core):
N
Nicolas Hennion 已提交
2291
            return 'CRITICAL'
2292
        elif variable > limits.getProcessWarning(stat=stat, core=core):
N
Nicolas Hennion 已提交
2293
            return 'WARNING'
2294
        elif variable > limits.getProcessCareful(stat=stat, core=core):
N
Nicolas Hennion 已提交
2295 2296 2297 2298
            return 'CAREFUL'

        return 'OK'

2299 2300
    def __getProcessCpuColor(self, current=0, max=100, core=1):
        return self.__colors_list[self.__getProcessAlert(current, max, 'CPU', core)]
N
Nicolas Hennion 已提交
2301

2302 2303
    def __getProcessCpuColor2(self, current=0, max=100, core=1):
        return self.__colors_list2[self.__getProcessAlert(current, max, 'CPU', core)]
N
Nicolas Hennion 已提交
2304 2305 2306 2307 2308 2309 2310

    def __getProcessMemColor(self, current=0, max=100):
        return self.__colors_list[self.__getProcessAlert(current, max, 'MEM')]

    def __getProcessMemColor2(self, current=0, max=100):
        return self.__colors_list2[self.__getProcessAlert(current, max, 'MEM')]

2311 2312
    def __getMonitoredAlert(self, nbprocess=0, countmin=None, countmax=None):
        # If count is not defined, not monitoring the number of processes
2313 2314 2315 2316
        if countmin is None:
            countmin = nbprocess
        if countmax is None:
            countmax = nbprocess
2317 2318 2319 2320 2321 2322
        if nbprocess > 0:
            if int(countmin) <= int(nbprocess) <= int(countmax):
                return 'OK'
            else:
                return 'WARNING'
        else:
N
Nicolas Hennion 已提交
2323
            if int(countmin) == 0:
2324 2325 2326
                return 'OK'
            else:
                return 'CRITICAL'
2327 2328 2329

    def __getMonitoredColor(self, nbprocess=0, countmin=1, countmax=1):
        return self.__colors_list2[self.__getMonitoredAlert(nbprocess, countmin, countmax)]
2330

2331
    def __getkey(self, window):
2332
        """
2333
        A getKey function to catch ESC key AND Numlock key (issue #163)
2334
        """
A
asergi 已提交
2335
        keycode = [0, 0]
2336 2337
        keycode[0] = window.getch()
        keycode[1] = window.getch()
A
asergi 已提交
2338 2339

        if keycode[0] == 27 and keycode[1] != -1:
2340 2341 2342 2343
            # Do not escape on specials keys
            return -1
        else:
            return keycode[0]
A
asergi 已提交
2344

A
asergi 已提交
2345 2346
    def __catchKey(self):
        # Get key
2347 2348
        #~ self.pressedkey = self.term_window.getch()
        self.pressedkey = self.__getkey(self.term_window)
A
asergi 已提交
2349

A
asergi 已提交
2350
        # Actions...
M
Markus Roth 已提交
2351
        if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
A
asergi 已提交
2352 2353
            # 'ESC'|'q' > Quit
            end()
M
Markus Roth 已提交
2354
        elif self.pressedkey == ord('1'):
2355 2356
            # '1' > Switch between CPU and PerCPU information
            self.percpu_tag = not self.percpu_tag
M
Markus Roth 已提交
2357
        elif self.pressedkey == ord('a'):
A
asergi 已提交
2358 2359
            # 'a' > Sort processes automatically
            self.setProcessSortedBy('auto')
M
Markus Roth 已提交
2360
        elif self.pressedkey == ord('b'):
A
asergi 已提交
2361
            # 'b' > Switch between bit/s and Byte/s for network IO
2362
            self.net_byteps_tag = not self.net_byteps_tag
M
Markus Roth 已提交
2363
        elif self.pressedkey == ord('c'):
A
asergi 已提交
2364 2365
            # 'c' > Sort processes by CPU usage
            self.setProcessSortedBy('cpu_percent')
M
Markus Roth 已提交
2366
        elif self.pressedkey == ord('d') and diskio_tag:
A
asergi 已提交
2367 2368
            # 'd' > Show/hide disk I/O stats
            self.diskio_tag = not self.diskio_tag
M
Markus Roth 已提交
2369
        elif self.pressedkey == ord('f') and fs_tag:
A
asergi 已提交
2370 2371
            # 'f' > Show/hide fs stats
            self.fs_tag = not self.fs_tag
M
Markus Roth 已提交
2372
        elif self.pressedkey == ord('h'):
A
asergi 已提交
2373 2374
            # 'h' > Show/hide help
            self.help_tag = not self.help_tag
M
Markus Roth 已提交
2375
        elif self.pressedkey == ord('i') and psutil_get_io_counter_tag:
A
asergi 已提交
2376
            # 'i' > Sort processes by IO rate (not available on OS X)
2377
            self.setProcessSortedBy('io_counters')
M
Markus Roth 已提交
2378
        elif self.pressedkey == ord('l'):
A
asergi 已提交
2379 2380
            # 'l' > Show/hide log messages
            self.log_tag = not self.log_tag
M
Markus Roth 已提交
2381
        elif self.pressedkey == ord('m'):
A
asergi 已提交
2382
            # 'm' > Sort processes by MEM usage
2383
            self.setProcessSortedBy('memory_percent')
M
Markus Roth 已提交
2384
        elif self.pressedkey == ord('n') and network_tag:
A
asergi 已提交
2385 2386
            # 'n' > Show/hide network stats
            self.network_tag = not self.network_tag
M
Markus Roth 已提交
2387
        elif self.pressedkey == ord('p'):
A
asergi 已提交
2388
            # 'p' > Sort processes by name
2389
            self.setProcessSortedBy('name')
M
Markus Roth 已提交
2390
        elif self.pressedkey == ord('s'):
A
asergi 已提交
2391
            # 's' > Show/hide sensors stats (Linux-only)
2392
            self.sensors_tag = not self.sensors_tag
M
Markus Roth 已提交
2393
        elif self.pressedkey == ord('t'):
2394 2395
            # 't' > View network traffic as combination
            self.network_stats_combined = not self.network_stats_combined
M
Markus Roth 已提交
2396
        elif self.pressedkey == ord('u'):
2397 2398
            # 'u' > View cumulative network IO
            self.network_stats_cumulative = not self.network_stats_cumulative
M
Markus Roth 已提交
2399
        elif self.pressedkey == ord('w'):
2400 2401
            # 'w' > Delete finished warning logs
            logs.clean()
M
Markus Roth 已提交
2402
        elif self.pressedkey == ord('x'):
2403
            # 'x' > Delete finished warning and critical logs
A
asergi 已提交
2404
            logs.clean(critical=True)
M
Markus Roth 已提交
2405
        elif self.pressedkey == ord('y'):
M
MendelGusmao 已提交
2406 2407
            # 'y' > Show/hide hddtemp stats
            self.hddtemp_tag = not self.hddtemp_tag
A
asergi 已提交
2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418

        # Return the key code
        return self.pressedkey

    def end(self):
        # Shutdown the curses window
        curses.echo()
        curses.nocbreak()
        curses.curs_set(1)
        curses.endwin()

A
asergi 已提交
2419
    def display(self, stats, cs_status="None"):
2420 2421 2422 2423 2424
        """
        Display stats on the screen
        cs_status:
            "None": standalone or server mode
            "Connected": Client is connected to the server
2425
            "Disconnected": Client is disconnected from the server
2426
        """
N
Nicolas Hennion 已提交
2427 2428 2429
        # Get stats for processes (used in another functions for logs)
        processcount = stats.getProcessCount()
        processlist = stats.getProcessList(screen.getProcessSortedBy())
2430

F
fraoustin 已提交
2431 2432 2433 2434
        if not self.help_tag:
            # Display stats
            self.displaySystem(stats.getHost(), stats.getSystem())
            cpu_offset = self.displayCpu(stats.getCpu(), stats.getPerCpu(), processlist)
2435
            self.displayLoad(stats.getLoad(), stats.getCore(), processlist, cpu_offset)
F
fraoustin 已提交
2436
            self.displayMem(stats.getMem(), stats.getMemSwap(), processlist, cpu_offset)
2437 2438
            network_count = self.displayNetwork(stats.getNetwork(),
                                                error=stats.network_error_tag)
F
fraoustin 已提交
2439 2440 2441 2442
            sensors_count = self.displaySensors(stats.getSensors(),
                                                self.network_y + network_count)
            hddtemp_count = self.displayHDDTemp(stats.getHDDTemp(),
                                                self.network_y + network_count + sensors_count)
2443 2444
            diskio_count = self.displayDiskIO(stats.getDiskIO(), offset_y=self.network_y +
                                              sensors_count + network_count + hddtemp_count,
F
fraoustin 已提交
2445
                                              error=stats.diskio_error_tag)
2446 2447
            fs_count = self.displayFs(stats.getFs(), self.network_y + sensors_count +
                                      network_count + diskio_count + hddtemp_count)
F
fraoustin 已提交
2448
            log_count = self.displayLog(self.network_y + sensors_count + network_count +
2449
                                        diskio_count + fs_count + hddtemp_count)
F
fraoustin 已提交
2450 2451 2452
            self.displayProcess(processcount, processlist, stats.getSortedBy(),
                                log_count=log_count, core=stats.getCore(), cs_status=cs_status)
            self.displayCaption(cs_status=cs_status)
A
asergi 已提交
2453
        self.displayHelp(core=stats.getCore())
2454 2455
        self.displayBat(stats.getBatPercent())
        self.displayNow(stats.getNow())
A
asergi 已提交
2456 2457 2458 2459 2460

    def erase(self):
        # Erase the content of the screen
        self.term_window.erase()

A
asergi 已提交
2461
    def flush(self, stats, cs_status="None"):
2462 2463 2464 2465 2466
        """
        Clear and update screen
        cs_status:
            "None": standalone or server mode
            "Connected": Client is connected to the server
2467
            "Disconnected": Client is disconnected from the server
2468
        """
A
asergi 已提交
2469
        self.erase()
A
asergi 已提交
2470
        self.display(stats, cs_status=cs_status)
A
asergi 已提交
2471

A
asergi 已提交
2472
    def update(self, stats, cs_status="None"):
2473 2474 2475 2476 2477
        """
        Update the screen and wait __refresh_time sec / catch key every 100 ms
        cs_status:
            "None": standalone or server mode
            "Connected": Client is connected to the server
2478
            "Disconnected": Client is disconnected from the server
2479
        """
2480
        # Flush display
A
asergi 已提交
2481
        self.flush(stats, cs_status=cs_status)
A
asergi 已提交
2482 2483 2484

        # Wait
        countdown = Timer(self.__refresh_time)
A
asergi 已提交
2485
        while not countdown.finished():
A
asergi 已提交
2486 2487 2488
            # Getkey
            if self.__catchKey() > -1:
                # flush display
A
asergi 已提交
2489
                self.flush(stats, cs_status=cs_status)
A
asergi 已提交
2490 2491 2492 2493 2494 2495 2496 2497 2498
            # Wait 100ms...
            curses.napms(100)

    def displaySystem(self, host, system):
        # System information
        if not host or not system:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
2499 2500 2501 2502 2503 2504 2505 2506 2507
        if host['os_name'] == "Linux":
            system_msg = _("{0} {1} with {2} {3} on {4}").format(
                system['linux_distro'], system['platform'],
                system['os_name'], system['os_version'],
                host['hostname'])
        else:
            system_msg = _("{0} {1} {2} on {3}").format(
                system['os_name'], system['os_version'],
                system['platform'], host['hostname'])
A
asergi 已提交
2508
        if (screen_y > self.system_y and
2509
            screen_x > self.system_x + len(system_msg)):
A
asergi 已提交
2510 2511
            center = (screen_x // 2) - len(system_msg) // 2
            self.term_window.addnstr(self.system_y, self.system_x + center,
A
asergi 已提交
2512 2513
                                     system_msg, 80, curses.A_UNDERLINE)

N
Nicolas Hennion 已提交
2514
    def displayCpu(self, cpu, percpu, proclist):
N
Nicolas Hennion 已提交
2515
        # Get screen size
A
asergi 已提交
2516 2517
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
2518

N
Nicolas Hennion 已提交
2519 2520
        # Is it possible to display extended stats ?
        # If yes then tag_extendedcpu = True
N
Nicolas Hennion 已提交
2521
        tag_extendedcpu = screen_x > self.cpu_x + 79 + 14
N
Nicolas Hennion 已提交
2522

2523
        # Is it possible to display per-CPU stats ? Do you want it ?
N
Nicolas Hennion 已提交
2524
        # If yes then tag_percpu = True
A
asergi 已提交
2525
        if self.percpu_tag:
F
fraoustin 已提交
2526
            tag_percpu = screen_x > self.cpu_x + 79 + (len(percpu) - 1) * 10
N
Nicolas Hennion 已提交
2527 2528
        else:
            tag_percpu = False
2529

A
asergi 已提交
2530 2531
        # compute x offset
        if tag_percpu:
F
fraoustin 已提交
2532
            offset_x = (len(percpu) - 1) * 8
A
asergi 已提交
2533 2534
        elif tag_extendedcpu:
            offset_x = 16
N
Nicolas Hennion 已提交
2535
        else:
2536
            offset_x = 0
A
asergi 已提交
2537

2538
        # Log
N
Nicolas Hennion 已提交
2539
        if cpu:
2540
            logs.add(self.__getCpuAlert(cpu['user'], stat="USER"), "CPU user",
N
Nicolas Hennion 已提交
2541
                     cpu['user'], proclist)
2542
            logs.add(self.__getCpuAlert(cpu['system'], stat="SYSTEM"), "CPU system",
N
Nicolas Hennion 已提交
2543
                     cpu['system'], proclist)
2544
            if 'iowait' in cpu:
2545 2546
                logs.add(self.__getCpuAlert(cpu['iowait'], stat="IOWAIT"), "CPU IOwait",
                         cpu['iowait'], proclist)
2547

A
asergi 已提交
2548
        # Display per-CPU stats
A
asergi 已提交
2549 2550
        if screen_y > self.cpu_y + 5 and tag_percpu:
            self.term_window.addnstr(self.cpu_y, self.cpu_x, _("PerCPU"), 6,
N
Nicolas Hennion 已提交
2551 2552 2553 2554 2555 2556 2557 2558
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)

            if not percpu:
                self.term_window.addnstr(self.cpu_y + 1, self.cpu_x,
                                         _("Compute data..."), 15)
                return 0

A
asergi 已提交
2559
            self.term_window.addnstr(self.cpu_y + 1, self.cpu_x,
2560
                                     _("user:"), 7)
A
asergi 已提交
2561 2562
            self.term_window.addnstr(self.cpu_y + 2, self.cpu_x,
                                     _("system:"), 7)
2563 2564 2565 2566 2567 2568
            if 'iowait' in percpu[0]:
                self.term_window.addnstr(self.cpu_y + 3, self.cpu_x,
                                         _("iowait:"), 7)
            else:
                self.term_window.addnstr(self.cpu_y + 3, self.cpu_x,
                                         _("idle:"), 7)
N
Nicolas Hennion 已提交
2569 2570

            for i in range(len(percpu)):
A
asergi 已提交
2571 2572 2573 2574
                # percentage of usage
                self.term_window.addnstr(
                    self.cpu_y, self.cpu_x + 8 + i * 8,
                    format((100 - percpu[i]['idle']) / 100, '>6.1%'), 6)
N
Nicolas Hennion 已提交
2575

A
asergi 已提交
2576 2577 2578 2579
                # user
                self.term_window.addnstr(
                    self.cpu_y + 1, self.cpu_x + 8 + i * 8,
                    format(percpu[i]['user'] / 100, '>6.1%'), 6,
A
asergi 已提交
2580
                    self.__getCpuColor2(percpu[i]['user'], stat='user'))
A
asergi 已提交
2581 2582 2583 2584 2585

                # system
                self.term_window.addnstr(
                    self.cpu_y + 2, self.cpu_x + 8 + i * 8,
                    format(percpu[i]['system'] / 100, '>6.1%'), 6,
A
asergi 已提交
2586
                    self.__getCpuColor2(percpu[i]['system'], stat='system'))
A
asergi 已提交
2587

N
Nicolas Hennion 已提交
2588 2589
                # If the IOWait stat is available then display it
                # else display the IDLE stat
2590 2591 2592 2593 2594
                if 'iowait' in percpu[i]:
                    # iowait
                    self.term_window.addnstr(
                        self.cpu_y + 3, self.cpu_x + 8 + i * 8,
                        format(percpu[i]['iowait'] / 100, '>6.1%'), 6,
A
asergi 已提交
2595
                        self.__getCpuColor2(percpu[i]['iowait'], stat='iowait'))
2596 2597 2598 2599 2600
                else:
                    # idle
                    self.term_window.addnstr(
                        self.cpu_y + 3, self.cpu_x + 8 + i * 8,
                        format(percpu[i]['idle'] / 100, '>6.1%'), 6)
A
asergi 已提交
2601 2602

        # display CPU summary information
A
asergi 已提交
2603 2604
        elif screen_y > self.cpu_y + 5 and screen_x > self.cpu_x + 18:
            self.term_window.addnstr(self.cpu_y, self.cpu_x, _("CPU"), 3,
A
asergi 已提交
2605 2606 2607 2608 2609 2610 2611 2612
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)

            if not cpu:
                self.term_window.addnstr(self.cpu_y + 1, self.cpu_x,
                                         _("Compute data..."), 15)
                return 0

A
asergi 已提交
2613 2614 2615 2616
            # percentage of usage
            cpu_percent = (100 - cpu['idle']) / 100
            self.term_window.addnstr(self.cpu_y, self.cpu_x + 8,
                                     format(cpu_percent, '>6.1%'), 6)
2617

2618
            y = 1
A
asergi 已提交
2619
            # user
2620 2621
            self.term_window.addnstr(self.cpu_y + y, self.cpu_x, _("user:"), 5)
            self.term_window.addnstr(self.cpu_y + y, self.cpu_x + 8,
A
asergi 已提交
2622
                                     format(cpu['user'] / 100, '>6.1%'), 6,
A
asergi 已提交
2623
                                     self.__getCpuColor(cpu['user'], stat='user'))
2624
            y += 1
A
asergi 已提交
2625

A
asergi 已提交
2626
            # system
N
Nicolas Hennion 已提交
2627
            if 'system' in cpu:
2628 2629 2630 2631
                self.term_window.addnstr(self.cpu_y + y, self.cpu_x,
                                         _("system:"), 7)
                self.term_window.addnstr(self.cpu_y + y, self.cpu_x + 8,
                                         format(cpu['system'] / 100, '>6.1%'), 6,
A
asergi 已提交
2632
                                         self.__getCpuColor(cpu['system'], stat='system'))
2633
                y += 1
A
asergi 已提交
2634 2635

            # idle
2636 2637
            self.term_window.addnstr(self.cpu_y + y, self.cpu_x, _("idle:"), 5)
            self.term_window.addnstr(self.cpu_y + y, self.cpu_x + 8,
A
asergi 已提交
2638
                                     format(cpu['idle'] / 100, '>6.1%'), 6)
2639
            y += 1
A
asergi 已提交
2640 2641 2642

            # display extended CPU stats when space is available
            if screen_y > self.cpu_y + 5 and tag_extendedcpu:
A
asergi 已提交
2643

2644
                y = 1
N
Nicolas Hennion 已提交
2645
                if 'nice' in cpu:
2646 2647 2648
                    # nice
                    self.term_window.addnstr(self.cpu_y + y, self.cpu_x + 16,
                                             _("nice:"), 5)
A
asergi 已提交
2649
                    self.term_window.addnstr(
2650
                        self.cpu_y + y, self.cpu_x + 24,
A
asergi 已提交
2651
                        format(cpu['nice'] / 100, '>6.1%'), 6)
2652 2653
                    y += 1

N
Nicolas Hennion 已提交
2654
                if 'iowait' in cpu:
2655 2656 2657
                    # iowait (Linux)
                    self.term_window.addnstr(self.cpu_y + y, self.cpu_x + 16,
                                             _("iowait:"), 7)
A
asergi 已提交
2658
                    self.term_window.addnstr(
2659
                        self.cpu_y + y, self.cpu_x + 24,
A
asergi 已提交
2660
                        format(cpu['iowait'] / 100, '>6.1%'), 6,
A
asergi 已提交
2661
                        self.__getCpuColor(cpu['iowait'], stat='iowait'))
2662
                    y += 1
A
asergi 已提交
2663

N
Nicolas Hennion 已提交
2664
                if 'irq' in cpu:
2665
                    # irq (Linux, FreeBSD)
2666 2667
                    self.term_window.addnstr(self.cpu_y + 3, self.cpu_x + 16,
                                             _("irq:"), 4)
A
asergi 已提交
2668 2669
                    self.term_window.addnstr(
                        self.cpu_y + 3, self.cpu_x + 24,
A
asergi 已提交
2670
                        format(cpu['irq'] / 100, '>6.1%'), 6)
2671
                    y += 1
A
asergi 已提交
2672

2673
        # return the x offset to display load
N
Nicolas Hennion 已提交
2674 2675
        return offset_x

N
Nicolas Hennion 已提交
2676
    def displayLoad(self, load, core, proclist, offset_x=0):
A
asergi 已提交
2677 2678 2679 2680 2681
        # Load %
        if not load:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
A
asergi 已提交
2682

2683
        loadblocksize = 15
A
asergi 已提交
2684

A
asergi 已提交
2685
        if (screen_y > self.load_y + 5 and
2686
            screen_x > self.load_x + offset_x + loadblocksize):
A
asergi 已提交
2687

A
asergi 已提交
2688 2689
            self.term_window.addnstr(self.load_y,
                                     self.load_x + offset_x, _("Load"), 4,
A
asergi 已提交
2690 2691
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
A
asergi 已提交
2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704
            self.term_window.addnstr(self.load_y, self.load_x + offset_x + 7,
                                     str(core) + _("-core"), 7)

            # 1 min
            self.term_window.addnstr(self.load_y + 1,
                                     self.load_x + offset_x, _("1 min:"), 6)
            self.term_window.addnstr(self.load_y + 1,
                                     self.load_x + offset_x + 8,
                                     format(load['min1'], '>5.2f'), 5)

            # 5 min
            self.term_window.addnstr(self.load_y + 2,
                                     self.load_x + offset_x, _("5 min:"), 6)
A
asergi 已提交
2705
            alert = self.__getLoadAlert(load['min5'], core)
N
Nicolas Hennion 已提交
2706
            logs.add(alert, "LOAD 5-min", load['min5'], proclist)
A
asergi 已提交
2707 2708 2709
            self.term_window.addnstr(self.load_y + 2,
                                     self.load_x + offset_x + 8,
                                     format(load['min5'], '>5.2f'), 5,
N
Nicolas Hennion 已提交
2710
                                     self.__getLoadColor(load['min5'], core))
A
asergi 已提交
2711 2712 2713 2714

            # 15 min
            self.term_window.addnstr(self.load_y + 3,
                                     self.load_x + offset_x, _("15 min:"), 7)
A
asergi 已提交
2715
            alert = self.__getLoadAlert(load['min15'], core)
N
Nicolas Hennion 已提交
2716
            logs.add(alert, "LOAD 15-min", load['min15'], proclist)
A
asergi 已提交
2717 2718 2719
            self.term_window.addnstr(self.load_y + 3,
                                     self.load_x + offset_x + 8,
                                     format(load['min15'], '>5.2f'), 5,
N
Nicolas Hennion 已提交
2720
                                     self.__getLoadColor(load['min15'], core))
A
asergi 已提交
2721

2722 2723
        # return the x offset to display mem
        return offset_x
A
asergi 已提交
2724

N
Nicolas Hennion 已提交
2725
    def displayMem(self, mem, memswap, proclist, offset_x=0):
A
asergi 已提交
2726
        # Memory
2727
        if not mem:
A
asergi 已提交
2728 2729 2730
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
2731 2732 2733 2734

        memblocksize = 45
        extblocksize = 15

2735 2736 2737
        # get the psutil version installed on the server, if in client mode
        if client_tag:
            server_psutil_version = stats.getPsutilVersion()
2738 2739
        else:
            server_psutil_version = ""
2740

A
asergi 已提交
2741
        if (screen_y > self.mem_y + 5 and
2742
            screen_x > self.mem_x + offset_x + memblocksize - extblocksize):
A
asergi 已提交
2743

A
asergi 已提交
2744 2745 2746
            # RAM
            self.term_window.addnstr(self.mem_y,
                                     self.mem_x + offset_x, _("Mem"), 8,
A
asergi 已提交
2747 2748
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
A
asergi 已提交
2749 2750 2751 2752 2753 2754

            # percentage of usage
            self.term_window.addnstr(self.mem_y, self.mem_x + offset_x + 6,
                                     format(mem['percent'] / 100, '>6.1%'), 6)

            # total
N
Nicolas Hennion 已提交
2755
            self.term_window.addnstr(self.mem_y + 1, self.mem_x + offset_x,
A
asergi 已提交
2756
                                     _("total:"), 6)
A
asergi 已提交
2757
            self.term_window.addnstr(
A
asergi 已提交
2758 2759 2760 2761 2762 2763 2764 2765
                self.mem_y + 1, self.mem_x + offset_x + 7,
                format(self.__autoUnit(mem['total']), '>5'), 5)

            # used
            alert = self.__getMemAlert(mem['used'], mem['total'])
            logs.add(alert, "MEM real", mem['used'], proclist)
            self.term_window.addnstr(self.mem_y + 2, self.mem_x + offset_x,
                                     _("used:"), 5)
A
asergi 已提交
2766
            self.term_window.addnstr(
A
asergi 已提交
2767 2768
                self.mem_y + 2, self.mem_x + offset_x + 7,
                format(self.__autoUnit(mem['used']), '>5'), 5,
N
Nicolas Hennion 已提交
2769
                self.__getMemColor(mem['used'], mem['total']))
A
asergi 已提交
2770 2771 2772 2773 2774 2775 2776 2777

            # free
            self.term_window.addnstr(self.mem_y + 3, self.mem_x + offset_x,
                                     _("free:"), 5)
            self.term_window.addnstr(
                self.mem_y + 3, self.mem_x + offset_x + 7,
                format(self.__autoUnit(mem['free']), '>5'), 5)

A
asergi 已提交
2778
            # Display extended informations if space is available
2779
            y = 0
A
asergi 已提交
2780
            if screen_x > self.mem_x + offset_x + memblocksize:
A
asergi 已提交
2781
                # active and inactive (UNIX; only available for psutil >= 0.6)
2782 2783
                if not is_Windows:
                    if server_psutil_version >= '0.6.0' or psutil_mem_vm:
2784 2785 2786 2787 2788 2789 2790
                        self.term_window.addnstr(self.mem_y + y,
                                                 self.mem_x + offset_x + 14,
                                                 _("active:"), 7)
                        self.term_window.addnstr(
                            self.mem_y + y, self.mem_x + offset_x + 24,
                            format(self.__autoUnit(mem['active']), '>5'), 5)
                        y += 1
2791

2792 2793 2794 2795 2796 2797 2798
                        self.term_window.addnstr(self.mem_y + y,
                                                 self.mem_x + offset_x + 14,
                                                 _("inactive:"), 9)
                        self.term_window.addnstr(
                            self.mem_y + y, self.mem_x + offset_x + 24,
                            format(self.__autoUnit(mem['inactive']), '>5'), 5)
                        y += 1
2799

A
asergi 已提交
2800
                # buffers & cached (Linux, BSD)
A
asergi 已提交
2801
                if is_Linux or is_BSD:
2802 2803 2804 2805 2806 2807 2808
                    self.term_window.addnstr(self.mem_y + y,
                                             self.mem_x + offset_x + 14,
                                             _("buffers:"), 8)
                    self.term_window.addnstr(
                        self.mem_y + y, self.mem_x + offset_x + 24,
                        format(self.__autoUnit(mem['buffers']), '>5'), 5)
                    y += 1
A
asergi 已提交
2809

2810 2811 2812 2813 2814 2815 2816
                    self.term_window.addnstr(self.mem_y + y,
                                             self.mem_x + offset_x + 14,
                                             _("cached:"), 7)
                    self.term_window.addnstr(
                        self.mem_y + y, self.mem_x + offset_x + 24,
                        format(self.__autoUnit(mem['cached']), '>5'), 5)
                    y += 1
2817 2818 2819
            else:
                # If space is NOT available then mind the gap...
                offset_x -= extblocksize
A
asergi 已提交
2820

2821
            if not memswap:
2822
                # If there is no swap stat, then do not display it
2823
                return 0
2824 2825
            if memswap['total'] == 0:
                # If swap is null, then do not display it
A
Alessio Sergi 已提交
2826
                return 0
2827

A
asergi 已提交
2828
            # Swap
A
asergi 已提交
2829 2830
            self.term_window.addnstr(self.mem_y,
                                     self.mem_x + offset_x + 32, _("Swap"), 4,
A
asergi 已提交
2831 2832
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
A
asergi 已提交
2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847

            # percentage of usage
            self.term_window.addnstr(
                self.mem_y, self.mem_x + offset_x + 38,
                format(memswap['percent'] / 100, '>6.1%'), 6)

            # total
            self.term_window.addnstr(self.mem_y + 1,
                                     self.mem_x + offset_x + 32,
                                     _("total:"), 6)
            self.term_window.addnstr(
                self.mem_y + 1, self.mem_x + offset_x + 39,
                format(self.__autoUnit(memswap['total']), '>5'), 8)

            # used
N
Nicolas Hennion 已提交
2848
            alert = self.__getSwapAlert(memswap['used'], memswap['total'])
N
Nicolas Hennion 已提交
2849
            logs.add(alert, "MEM swap", memswap['used'], proclist)
A
asergi 已提交
2850 2851 2852 2853 2854 2855
            self.term_window.addnstr(self.mem_y + 2,
                                     self.mem_x + offset_x + 32,
                                     _("used:"), 5)
            self.term_window.addnstr(
                self.mem_y + 2, self.mem_x + offset_x + 39,
                format(self.__autoUnit(memswap['used']), '>5'), 8,
N
Nicolas Hennion 已提交
2856
                self.__getSwapColor(memswap['used'], memswap['total']))
A
asergi 已提交
2857 2858 2859 2860 2861 2862 2863 2864

            # free
            self.term_window.addnstr(self.mem_y + 3,
                                     self.mem_x + offset_x + 32,
                                     _("free:"), 5)
            self.term_window.addnstr(
                self.mem_y + 3, self.mem_x + offset_x + 39,
                format(self.__autoUnit(memswap['free']), '>5'), 8)
A
asergi 已提交
2865

A
Alessio Sergi 已提交
2866
    def displayNetwork(self, network, error=False):
A
asergi 已提交
2867 2868
        """
        Display the network interface bitrate
2869
        If error = True, then display a grab error message
A
asergi 已提交
2870 2871 2872 2873 2874 2875
        Return the number of interfaces
        """
        if not self.network_tag:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
A
asergi 已提交
2876
        if screen_y > self.network_y + 3 and screen_x > self.network_x + 28:
A
asergi 已提交
2877
            self.term_window.addnstr(self.network_y, self.network_x,
A
asergi 已提交
2878
                                     _("Network"), 7, self.title_color if
A
asergi 已提交
2879
                                     self.hascolors else curses.A_UNDERLINE)
2880 2881 2882 2883 2884

            if self.network_stats_combined:
                column_name = "Rx+Tx"
                if not self.network_stats_cumulative:
                    column_name += "/s"
2885
                self.term_window.addnstr(self.network_y, self.network_x + 10,
2886
                                         format(_(column_name), '>13'), 13)
2887
            else:
2888 2889 2890 2891 2892
                rx_column_name = "Rx"
                tx_column_name = "Tx"
                if not self.network_stats_cumulative:
                    rx_column_name += "/s"
                    tx_column_name += "/s"
2893
                self.term_window.addnstr(self.network_y, self.network_x + 10,
2894 2895
                                         format(_(rx_column_name), '>5'), 5)
                self.term_window.addnstr(self.network_y, self.network_x + 18,
A
Alessio Sergi 已提交
2896
                                         format(_(tx_column_name), '>5'), 5)
A
asergi 已提交
2897

2898 2899 2900
            if error:
                # If there is a grab error
                self.term_window.addnstr(self.network_y + 1, self.network_x,
2901
                                         _("Cannot grab data..."), 20)
2902 2903 2904
                return 3
            elif not network:
                # or no data to display...
A
asergi 已提交
2905 2906 2907 2908 2909 2910 2911
                self.term_window.addnstr(self.network_y + 1, self.network_x,
                                         _("Compute data..."), 15)
                return 3

            # Adapt the maximum interface to the screen
            ret = 2
            net_num = min(screen_y - self.network_y - 3, len(network))
2912
            for i in range(0, net_num):
A
asergi 已提交
2913
                elapsed_time = max(1, self.__refresh_time)
A
asergi 已提交
2914 2915

                # network interface name
2916 2917
                #~ ifname = network[i]['interface_name'].encode('ascii', 'ignore').split(':')[0]
                ifname = network[i]['interface_name'].split(':')[0]
A
asergi 已提交
2918 2919 2920 2921
                if len(ifname) > 8:
                    ifname = '_' + ifname[-8:]
                self.term_window.addnstr(self.network_y + 1 + i,
                                         self.network_x, ifname, 8)
A
asergi 已提交
2922 2923 2924

                # Byte/s or bit/s
                if self.net_byteps_tag:
2925 2926
                    rx_per_sec = self.__autoUnit(network[i]['rx'] // elapsed_time)
                    tx_per_sec = self.__autoUnit(network[i]['tx'] // elapsed_time)
2927
                    # Combined, or total network traffic
2928 2929 2930 2931 2932 2933
                    # cx is combined rx + tx
                    cx_per_sec = self.__autoUnit(network[i]['cx'] // elapsed_time)
                    cumulative_rx = self.__autoUnit(network[i]['cumulative_rx'])
                    cumulative_tx = self.__autoUnit(network[i]['cumulative_tx'])
                    cumulative_cx = self.__autoUnit(network[i]['cumulative_cx'])

2934
                else:
2935
                    rx_per_sec = self.__autoUnit(
A
asergi 已提交
2936
                        network[i]['rx'] // elapsed_time * 8) + "b"
2937
                    tx_per_sec = self.__autoUnit(
A
asergi 已提交
2938
                        network[i]['tx'] // elapsed_time * 8) + "b"
2939 2940
                    # cx is combined rx + tx
                    cx_per_sec = self.__autoUnit(
2941
                        network[i]['cx'] // elapsed_time * 8) + "b"
2942
                    cumulative_rx = self.__autoUnit(
2943
                        network[i]['cumulative_rx'] * 8) + "b"
2944
                    cumulative_tx = self.__autoUnit(
2945
                        network[i]['cumulative_tx'] * 8) + "b"
2946
                    cumulative_cx = self.__autoUnit(
2947
                        network[i]['cumulative_cx'] * 8) + "b"
2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958

                if self.network_stats_cumulative:
                    rx = cumulative_rx
                    tx = cumulative_tx
                    cx = cumulative_cx
                else:
                    rx = rx_per_sec
                    tx = tx_per_sec
                    cx = cx_per_sec

                if not self.network_stats_combined:
2959 2960
                    # rx/s
                    self.term_window.addnstr(self.network_y + 1 + i,
2961 2962
                                             self.network_x + 8,
                                             format(rx, '>7'), 7)
2963 2964
                    # tx/s
                    self.term_window.addnstr(self.network_y + 1 + i,
2965 2966
                                             self.network_x + 16,
                                             format(tx, '>7'), 7)
2967 2968 2969
                else:
                    # cx/s (Combined, or total)
                    self.term_window.addnstr(self.network_y + 1 + i,
2970 2971
                                             self.network_x + 16,
                                             format(cx, '>7'), 7)
A
asergi 已提交
2972 2973 2974 2975
                ret = ret + 1
            return ret
        return 0

2976 2977
    def displaySensors(self, sensors, offset_y=0):
        """
A
asergi 已提交
2978
        Display the sensors stats (Linux-only)
2979 2980
        Return the number of sensors stats
        """
N
Nicolas Hennion 已提交
2981
        if not self.sensors_tag or not sensors:
2982 2983 2984 2985
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        self.sensors_y = offset_y
A
asergi 已提交
2986
        if screen_y > self.sensors_y + 3 and screen_x > self.sensors_x + 28:
2987 2988
            # Sensors header
            self.term_window.addnstr(self.sensors_y, self.sensors_x,
A
asergi 已提交
2989
                                     _("Sensors"), 7, self.title_color
2990
                                     if self.hascolors else curses.A_UNDERLINE)
A
asergi 已提交
2991 2992
            self.term_window.addnstr(self.sensors_y, self.sensors_x + 21,
                                     format(_("°C"), '>3'), 3)
2993 2994 2995 2996 2997 2998 2999

            # Adapt the maximum interface to the screen
            ret = 2
            sensors_num = min(screen_y - self.sensors_y - 3, len(sensors))
            for i in range(0, sensors_num):
                self.term_window.addnstr(
                    self.sensors_y + 1 + i, self.sensors_x,
3000
                    sensors[i]['label'], 21)
3001
                self.term_window.addnstr(
A
asergi 已提交
3002
                    self.sensors_y + 1 + i, self.sensors_x + 20,
N
Nicolas Hennion 已提交
3003 3004
                    format(sensors[i]['value'], '>3'), 3,
                    self.__getSensorsColor(sensors[i]['value']))
3005 3006 3007 3008
                ret = ret + 1
            return ret
        return 0

M
MendelGusmao 已提交
3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021
    def displayHDDTemp(self, hddtemp, offset_y=0):
        """
        Display the hddtemp stats
        Return the number of hddtemp stats
        """
        if not self.hddtemp_tag or not hddtemp:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        self.hddtemp_y = offset_y
        if screen_y > self.hddtemp_y + 3 and screen_x > self.hddtemp_x + 28:
            # hddtemp header
            self.term_window.addnstr(self.hddtemp_y, self.hddtemp_x,
3022
                                     _("HDD Temp"), 8, self.title_color
M
MendelGusmao 已提交
3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036
                                     if self.hascolors else curses.A_UNDERLINE)
            self.term_window.addnstr(self.hddtemp_y, self.hddtemp_x + 21,
                                     format(_("°C"), '>3'), 3)

            # Adapt the maximum interface to the screen
            ret = 2
            hddtemp_num = min(screen_y - self.hddtemp_y - 3, len(hddtemp))
            for i in range(0, hddtemp_num):
                self.term_window.addnstr(
                    self.hddtemp_y + 1 + i, self.hddtemp_x,
                    hddtemp[i]['label'], 21)
                self.term_window.addnstr(
                    self.hddtemp_y + 1 + i, self.hddtemp_x + 20,
                    format(hddtemp[i]['value'], '>3'), 3,
3037
                    self.__getHDDTempColor(hddtemp[i]['value']))
M
MendelGusmao 已提交
3038 3039 3040 3041
                ret = ret + 1
            return ret
        return 0

A
Alessio Sergi 已提交
3042
    def displayDiskIO(self, diskio, offset_y=0, error=False):
A
asergi 已提交
3043 3044 3045 3046 3047 3048
        # Disk input/output rate
        if not self.diskio_tag:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        self.diskio_y = offset_y
A
asergi 已提交
3049
        if screen_y > self.diskio_y + 3 and screen_x > self.diskio_x + 28:
A
asergi 已提交
3050 3051 3052 3053 3054
            self.term_window.addnstr(self.diskio_y, self.diskio_x,
                                     _("Disk I/O"), 8,
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
            self.term_window.addnstr(self.diskio_y, self.diskio_x + 10,
A
asergi 已提交
3055 3056 3057
                                     format(_("In/s"), '>5'), 5)
            self.term_window.addnstr(self.diskio_y, self.diskio_x + 18,
                                     format(_("Out/s"), '>5'), 5)
A
asergi 已提交
3058

3059 3060 3061
            if error:
                # If there is a grab error
                self.term_window.addnstr(self.diskio_y + 1, self.diskio_x,
3062
                                         _("Cannot grab data..."), 20)
3063 3064 3065
                return 3
            elif not diskio:
                # or no data to display...
A
asergi 已提交
3066 3067 3068 3069 3070 3071 3072
                self.term_window.addnstr(self.diskio_y + 1, self.diskio_x,
                                         _("Compute data..."), 15)
                return 3

            # Adapt the maximum disk to the screen
            disk = 0
            disk_num = min(screen_y - self.diskio_y - 3, len(diskio))
3073
            for disk in range(0, disk_num):
A
asergi 已提交
3074
                elapsed_time = max(1, self.__refresh_time)
A
asergi 已提交
3075 3076

                # partition name
A
asergi 已提交
3077 3078
                self.term_window.addnstr(
                    self.diskio_y + 1 + disk, self.diskio_x,
3079
                    diskio[disk]['disk_name'], 8)
A
asergi 已提交
3080 3081 3082

                # in/s
                ins = diskio[disk]['write_bytes'] // elapsed_time
A
asergi 已提交
3083 3084
                self.term_window.addnstr(
                    self.diskio_y + 1 + disk, self.diskio_x + 10,
A
asergi 已提交
3085 3086 3087 3088
                    format(self.__autoUnit(ins), '>5'), 5)

                # out/s
                outs = diskio[disk]['read_bytes'] // elapsed_time
A
asergi 已提交
3089
                self.term_window.addnstr(
A
asergi 已提交
3090 3091
                    self.diskio_y + 1 + disk, self.diskio_x + 18,
                    format(self.__autoUnit(outs), '>5'), 5)
A
asergi 已提交
3092 3093 3094 3095 3096 3097 3098 3099 3100 3101
            return disk + 3
        return 0

    def displayFs(self, fs, offset_y=0):
        # Filesystem stats
        if not fs or not self.fs_tag:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        self.fs_y = offset_y
A
asergi 已提交
3102
        if screen_y > self.fs_y + 3 and screen_x > self.fs_x + 28:
A
asergi 已提交
3103
            self.term_window.addnstr(self.fs_y, self.fs_x, _("Mount"), 5,
A
asergi 已提交
3104 3105
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
A
asergi 已提交
3106 3107 3108 3109
            self.term_window.addnstr(self.fs_y, self.fs_x + 9,
                                     format(_("Used"), '>6'), 6)
            self.term_window.addnstr(self.fs_y, self.fs_x + 17,
                                     format(_("Total"), '>6'), 6)
A
asergi 已提交
3110 3111 3112 3113

            # Adapt the maximum disk to the screen
            mounted = 0
            fs_num = min(screen_y - self.fs_y - 3, len(fs))
3114
            for mounted in range(0, fs_num):
A
asergi 已提交
3115
                # mount point
N
Nicolas Hennion 已提交
3116 3117
                if len(fs[mounted]['mnt_point']) > 8:
                    self.term_window.addnstr(
A
asergi 已提交
3118 3119
                        self.fs_y + 1 + mounted, self.fs_x,
                        '_' + fs[mounted]['mnt_point'][-7:], 8)
N
Nicolas Hennion 已提交
3120 3121
                else:
                    self.term_window.addnstr(
A
asergi 已提交
3122 3123 3124 3125
                        self.fs_y + 1 + mounted, self.fs_x,
                        fs[mounted]['mnt_point'], 8)

                # used
A
asergi 已提交
3126
                self.term_window.addnstr(
A
asergi 已提交
3127
                    self.fs_y + 1 + mounted, self.fs_x + 9,
N
Nicolas Hennion 已提交
3128 3129
                    format(self.__autoUnit(fs[mounted]['used']), '>6'), 6,
                    self.__getFsColor2(fs[mounted]['used'], fs[mounted]['size']))
A
asergi 已提交
3130 3131

                # total
A
asergi 已提交
3132
                self.term_window.addnstr(
A
asergi 已提交
3133
                    self.fs_y + 1 + mounted, self.fs_x + 17,
N
Nicolas Hennion 已提交
3134
                    format(self.__autoUnit(fs[mounted]['size']), '>6'), 6)
A
asergi 已提交
3135

A
asergi 已提交
3136 3137 3138 3139 3140 3141 3142 3143 3144 3145
            return mounted + 3
        return 0

    def displayLog(self, offset_y=0):
        # Logs
        if logs.len() == 0 or not self.log_tag:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        self.log_y = offset_y
A
asergi 已提交
3146
        if screen_y > self.log_y + 3 and screen_x > self.log_x + 79:
A
asergi 已提交
3147 3148 3149 3150
            self.log_y = max(offset_y, screen_y - 3 -
                             min(offset_y - 3, screen_y - self.log_y,
                                 logs.len()))
            logtodisplay_count = min(screen_y - self.log_y - 3, logs.len())
3151
            logmsg = _("WARNING|CRITICAL logs")
A
asergi 已提交
3152
            if logtodisplay_count > 1:
A
asergi 已提交
3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163
                logmsg += (_(" (lasts ") + str(logtodisplay_count) +
                           _(" entries)"))
            else:
                logmsg += _(" (one entry)")
            self.term_window.addnstr(self.log_y, self.log_x, logmsg, 79,
                                     self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)

            # Adapt the maximum log to the screen
            logcount = 0
            log = logs.get()
3164
            for logcount in range(0, logtodisplay_count):
A
asergi 已提交
3165
                logmsg = "  " + str(datetime.fromtimestamp(log[logcount][0]))
A
asergi 已提交
3166
                if log[logcount][1] > 0:
A
asergi 已提交
3167 3168 3169 3170 3171 3172 3173
                    logmark = ' '
                    logmsg += (" > " +
                               str(datetime.fromtimestamp(log[logcount][1])))
                else:
                    logmark = '~'
                    logmsg += " > " + "%19s" % "___________________"
                if log[logcount][3][:3] == "MEM":
3174
                    # Special display for MEMORY
A
asergi 已提交
3175 3176 3177 3178 3179
                    logmsg += " {0} ({1}/{2}/{3})".format(
                        log[logcount][3],
                        self.__autoUnit(log[logcount][6]),
                        self.__autoUnit(log[logcount][5]),
                        self.__autoUnit(log[logcount][4]))
3180 3181 3182 3183 3184 3185 3186 3187 3188
                elif log[logcount][3][:3] == "MON":
                    # Special display for monitored pocesses list
                    if (log[logcount][5] == 0):
                        logmsg += " No running process"
                    elif (log[logcount][5] == 1):
                        logmsg += " One running process"
                    else:
                        logmsg += " {0} running processes".format(
                            self.__autoUnit(log[logcount][5]))
A
asergi 已提交
3189 3190 3191 3192
                else:
                    logmsg += " {0} ({1:.1f}/{2:.1f}/{3:.1f})".format(
                        log[logcount][3], log[logcount][6],
                        log[logcount][5], log[logcount][4])
3193 3194 3195 3196 3197
                # Add the monitored process description
                if log[logcount][10] != "":
                    logmsg += " - {0}".format(log[logcount][10])
                elif log[logcount][9] != []:
                    # Add top processes
A
asergi 已提交
3198 3199
                    log_proc_name = log[logcount][9][0]['name']
                    logmsg += " - Top process: {0}".format(log_proc_name)
N
Nicolas Hennion 已提交
3200
                # Display the log
A
asergi 已提交
3201
                self.term_window.addnstr(self.log_y + 1 + logcount,
N
Nicolas Hennion 已提交
3202
                                         self.log_x, logmsg, len(logmsg))
A
asergi 已提交
3203 3204 3205 3206 3207 3208
                self.term_window.addnstr(self.log_y + 1 + logcount,
                                         self.log_x, logmark, 1,
                                         self.__colors_list[log[logcount][2]])
            return logcount + 3
        return 0

3209 3210 3211 3212
    def getProcessColumnColor(self, column, sortedby):
        """
        Return the Process title colr depending of:
        self.getProcessSortedBy() -> User sort choice
A
asergi 已提交
3213
                         sortedby -> System last sort
3214
        """
A
asergi 已提交
3215
        if self.getProcessSortedBy() == 'auto' and sortedby == column:
3216
            return curses.A_UNDERLINE
A
asergi 已提交
3217
        elif self.getProcessSortedBy() == column:
3218 3219 3220 3221
            return self.title_color if self.hascolors else curses.A_UNDERLINE
        else:
            return 0

3222 3223
    def displayProcess(self, processcount, processlist, sortedby='',
                       log_count=0, core=1, cs_status="None"):
3224 3225 3226 3227 3228 3229 3230 3231 3232 3233
        """
        Display the processese:
        * summary
        * monitored processes list (optionnal)
        * processes detailed list
        cs_status:
            "None": standalone or server mode
            "Connected": Client is connected to the server
            "Disconnected": Client is disconnected from the server
        """
A
asergi 已提交
3234 3235 3236 3237 3238
        # Process
        if not processcount:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
M
MendelGusmao 已提交
3239
        # If there is no network & diskio & fs & sensors stats & hddtemp stats
A
asergi 已提交
3240 3241
        # then increase process window
        if (not self.network_tag and
3242 3243 3244 3245
            not self.diskio_tag and
            not self.fs_tag and
            not self.sensors_tag and
            not self.hddtemp_tag):
A
asergi 已提交
3246 3247 3248
            process_x = 0
        else:
            process_x = self.process_x
A
asergi 已提交
3249

3250
        #******************
A
asergi 已提交
3251
        # Processes summary
3252
        #******************
3253 3254 3255 3256 3257 3258
        if not self.process_tag:
            self.term_window.addnstr(self.process_y, process_x,
                                     _("Processes (disabled)"),
                                     20, self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
            return 0
A
asergi 已提交
3259
        if screen_y > self.process_y + 4 and screen_x > process_x + 48:
A
asergi 已提交
3260 3261 3262 3263 3264 3265 3266 3267
            self.term_window.addnstr(self.process_y, process_x, _("Processes"),
                                     9, self.title_color if self.hascolors else
                                     curses.A_UNDERLINE)
            other = (processcount['total'] -
                     stats.getProcessCount()['running'] -
                     stats.getProcessCount()['sleeping'])
            self.term_window.addnstr(
                self.process_y, process_x + 10,
A
asergi 已提交
3268
                '{0:>3}, {1:>2} {2}, {3:>3} {4}, {5:>2} {6}'.format(
A
asergi 已提交
3269 3270 3271 3272 3273 3274 3275 3276
                    str(processcount['total']),
                    str(processcount['running']),
                    _("running"),
                    str(processcount['sleeping']),
                    _("sleeping"),
                    str(other),
                    _("other")), 42)

3277 3278 3279 3280 3281 3282 3283
        # Sort info
        # self.getProcessSortedBy() -> User sort choice
        #                  sortedby -> System last sort
        if self.getProcessSortedBy() == 'auto':
            sortmsg = _("sorted automatically")
        else:
            sortmsg = _("sorted by ") + sortedby
A
asergi 已提交
3284 3285 3286
        if (screen_y > self.process_y + 4 and
            screen_x > process_x + 49 + len(sortmsg)):
            self.term_window.addnstr(self.process_y, 76, sortmsg, len(sortmsg))
3287

3288
        #*************************
3289
        # Monitored processes list
3290
        #*************************
3291
        monitor_y = self.process_y
N
Nicolas Hennion 已提交
3292
        if (len(monitors) > 0 and
3293 3294 3295 3296 3297 3298 3299 3300 3301
            screen_y > self.process_y + 5 + len(monitors) and
            screen_x > process_x + 49):
            # Add space between process summary and monitored processes list
            monitor_y += 1
            item = 0
            for processes in monitors:
                # Display the monitored processes list (one line per monitored processes)
                monitor_y += 1
                # Search monitored processes by a regular expression
3302
                monitoredlist = [p for p in processlist if re.search(monitors.regex(item), p['cmdline']) is not None]
3303
                # Build and print non optional message
3304
                monitormsg1 = "{0:>16} {1:3} {2:13}".format(
3305
                    monitors.description(item)[0:15],
3306
                    len(monitoredlist) if len(monitoredlist) > 1 else "",
3307
                    _("RUNNING") if len(monitoredlist) > 0 else _("NOT RUNNING"))
N
Nicolas Hennion 已提交
3308
                self.term_window.addnstr(monitor_y, self.process_x,
3309
                                         monitormsg1, screen_x - process_x,
N
Nicolas Hennion 已提交
3310
                                         self.__getMonitoredColor(len(monitoredlist),
3311 3312 3313
                                                                  monitors.countmin(item),
                                                                  monitors.countmax(item)))
                # Build and print optional message
3314 3315 3316
                if len(monitoredlist) > 0:
                    if (cs_status.lower() == "none" and
                        monitors.command(item) is not None):
3317 3318
                        # Execute the user command line
                        try:
3319
                            cmdret = subprocess.check_output(monitors.command(item), shell=True)
3320 3321
                        except subprocess.CalledProcessError:
                            cmdret = _("Error: ") + monitors.command(item)
3322 3323
                        except Exception:
                            cmdret = _("Cannot execute command")
3324 3325 3326
                    else:
                        # By default display CPU and MEM %
                        cmdret = "CPU: {0:.1f}% / MEM: {1:.1f}%".format(
3327 3328
                            sum([p['cpu_percent'] for p in monitoredlist]),
                            sum([p['memory_percent'] for p in monitoredlist]))
3329 3330
                else:
                    cmdret = ""
N
Nicolas Hennion 已提交
3331 3332 3333
                    # cmdret = "{0} / {1} / {2}".format(len(monitoredlist),
                    #                                   monitors.countmin(item),
                    #                                   monitors.countmax(item))
3334 3335 3336 3337

                monitormsg2 = "{0}".format(cmdret)
                self.term_window.addnstr(monitor_y, self.process_x + 35,
                                         monitormsg2, screen_x - process_x - 35)
3338

3339
                # Generate log
N
Nicolas Hennion 已提交
3340
                logs.add(self.__getMonitoredAlert(len(monitoredlist),
3341
                                                  monitors.countmin(item),
N
Nicolas Hennion 已提交
3342 3343 3344
                                                  monitors.countmax(item)),
                         "MON_" + str(item + 1),
                         len(monitoredlist),
3345 3346
                         proc_list=monitoredlist,
                         proc_desc=monitors.description(item))
3347

N
Nicolas Hennion 已提交
3348
                # Next...
3349 3350
                item += 1

3351
        #*****************
A
asergi 已提交
3352
        # Processes detail
3353
        #*****************
3354
        if screen_y > monitor_y + 4 and screen_x > process_x + 49:
3355 3356 3357 3358 3359 3360
            tag_pid = False
            tag_uid = False
            tag_nice = False
            tag_status = False
            tag_proc_time = False
            tag_io = False
3361
            # tag_tcpudp = False
A
asergi 已提交
3362

3363 3364 3365 3366 3367 3368 3369 3370 3371 3372
            if screen_x > process_x + 55:
                tag_pid = True
            if screen_x > process_x + 64:
                tag_uid = True
            if screen_x > process_x + 70:
                tag_nice = True
            if screen_x > process_x + 74:
                tag_status = True
            if screen_x > process_x + 77:
                tag_proc_time = True
N
Nicolas Hennion 已提交
3373
            if screen_x > process_x + 92:
3374
                tag_io = True
N
Nicolas Hennion 已提交
3375 3376
            if not psutil_get_io_counter_tag:
                tag_io = False
3377 3378
            # if screen_x > process_x + 107:
            #     tag_tcpudp = True
3379

A
asergi 已提交
3380 3381
            # VMS
            self.term_window.addnstr(
3382
                monitor_y + 2, process_x,
A
asergi 已提交
3383
                format(_("VIRT"), '>5'), 5)
A
asergi 已提交
3384 3385
            # RSS
            self.term_window.addnstr(
3386
                monitor_y + 2, process_x + 6,
A
asergi 已提交
3387
                format(_("RES"), '>5'), 5)
A
asergi 已提交
3388 3389
            # CPU%
            self.term_window.addnstr(
3390
                monitor_y + 2, process_x + 12,
A
asergi 已提交
3391
                format(_("CPU%"), '>5'), 5,
3392
                self.getProcessColumnColor('cpu_percent', sortedby))
A
asergi 已提交
3393 3394
            # MEM%
            self.term_window.addnstr(
3395
                monitor_y + 2, process_x + 18,
A
asergi 已提交
3396
                format(_("MEM%"), '>5'), 5,
3397
                self.getProcessColumnColor('memory_percent', sortedby))
A
asergi 已提交
3398
            process_name_x = 24
A
asergi 已提交
3399 3400 3401 3402
            # If screen space (X) is available then:
            # PID
            if tag_pid:
                self.term_window.addnstr(
3403
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3404 3405
                    format(_("PID"), '>5'), 5)
                process_name_x += 6
A
asergi 已提交
3406 3407 3408
            # UID
            if tag_uid:
                self.term_window.addnstr(
3409
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3410 3411
                    _("USER"), 4)
                process_name_x += 11
A
asergi 已提交
3412 3413 3414
            # NICE
            if tag_nice:
                self.term_window.addnstr(
3415
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3416
                    format(_("NI"), '>3'), 3)
A
asergi 已提交
3417 3418 3419 3420
                process_name_x += 4
            # STATUS
            if tag_status:
                self.term_window.addnstr(
3421
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3422
                    _("S"), 1)
A
asergi 已提交
3423
                process_name_x += 2
A
asergi 已提交
3424 3425 3426
            # TIME+
            if tag_proc_time:
                self.term_window.addnstr(
3427
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3428
                    format(_("TIME+"), '>8'), 8)
A
asergi 已提交
3429
                process_name_x += 9
3430 3431 3432
            # IO
            if tag_io:
                self.term_window.addnstr(
3433
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3434
                    format(_("IOR/s"), '>5'), 5,
3435
                    self.getProcessColumnColor('io_counters', sortedby))
A
asergi 已提交
3436
                process_name_x += 6
3437
                self.term_window.addnstr(
3438
                    monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3439
                    format(_("IOW/s"), '>5'), 5,
3440
                    self.getProcessColumnColor('io_counters', sortedby))
A
asergi 已提交
3441
                process_name_x += 6
3442
            # TCP/UDP
3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453
            # if tag_tcpudp:
            #     self.term_window.addnstr(
            #         monitor_y + 2, process_x + process_name_x,
            #         format(_("TCP"), '>5'), 5,
            #         self.getProcessColumnColor('tcp', sortedby))
            #     process_name_x += 6
            #     self.term_window.addnstr(
            #         monitor_y + 2, process_x + process_name_x,
            #         format(_("UDP"), '>5'), 5,
            #         self.getProcessColumnColor('udp', sortedby))
            #     process_name_x += 6
A
asergi 已提交
3454 3455
            # PROCESS NAME
            self.term_window.addnstr(
3456
                monitor_y + 2, process_x + process_name_x,
A
asergi 已提交
3457
                _("NAME"), 12, curses.A_UNDERLINE
3458
                if sortedby == 'name' else 0)
A
asergi 已提交
3459 3460 3461

            # If there is no data to display...
            if not processlist:
3462
                self.term_window.addnstr(monitor_y + 3, self.process_x,
A
asergi 已提交
3463 3464 3465
                                         _("Compute data..."), 15)
                return 6

3466
            # Display the processes list
3467 3468
            # How many processes are going to be displayed ?
            proc_num = min(screen_y - monitor_y - log_count - 5,
A
asergi 已提交
3469
                           len(processlist))
N
Nicolas Hennion 已提交
3470

3471
            # Loop to display processes
3472
            for processes in range(0, proc_num):
A
asergi 已提交
3473
                # VMS
A
asergi 已提交
3474 3475
                process_size = processlist[processes]['memory_info'][1]
                self.term_window.addnstr(
3476
                    monitor_y + 3 + processes, process_x,
3477
                    format(self.__autoUnit(process_size, low_precision=True),
J
Jon Renner 已提交
3478
                           '>5'), 5)
A
asergi 已提交
3479
                # RSS
A
asergi 已提交
3480 3481
                process_resident = processlist[processes]['memory_info'][0]
                self.term_window.addnstr(
3482
                    monitor_y + 3 + processes, process_x + 6,
3483
                    format(self.__autoUnit(process_resident, low_precision=True),
J
Jon Renner 已提交
3484
                           '>5'), 5)
A
asergi 已提交
3485 3486
                # CPU%
                cpu_percent = processlist[processes]['cpu_percent']
A
asergi 已提交
3487
                self.term_window.addnstr(
3488
                    monitor_y + 3 + processes, process_x + 12,
A
asergi 已提交
3489
                    format(cpu_percent, '>5.1f'), 5,
3490
                    self.__getProcessCpuColor2(cpu_percent, core=core))
A
asergi 已提交
3491
                # MEM%
A
asergi 已提交
3492 3493
                memory_percent = processlist[processes]['memory_percent']
                self.term_window.addnstr(
3494
                    monitor_y + 3 + processes, process_x + 18,
A
asergi 已提交
3495 3496
                    format(memory_percent, '>5.1f'), 5,
                    self.__getProcessMemColor2(memory_percent))
A
asergi 已提交
3497 3498 3499 3500 3501
                # If screen space (X) is available then:
                # PID
                if tag_pid:
                    pid = processlist[processes]['pid']
                    self.term_window.addnstr(
3502
                        monitor_y + 3 + processes, process_x + 24,
A
asergi 已提交
3503
                        format(str(pid), '>5'), 5)
A
asergi 已提交
3504 3505
                # UID
                if tag_uid:
3506
                    uid = processlist[processes]['username']
A
asergi 已提交
3507
                    self.term_window.addnstr(
3508
                        monitor_y + 3 + processes, process_x + 30,
A
asergi 已提交
3509
                        str(uid), 9)
A
asergi 已提交
3510 3511 3512 3513
                # NICE
                if tag_nice:
                    nice = processlist[processes]['nice']
                    self.term_window.addnstr(
3514
                        monitor_y + 3 + processes, process_x + 41,
A
asergi 已提交
3515
                        format(str(nice), '>3'), 3)
A
asergi 已提交
3516 3517 3518 3519
                # STATUS
                if tag_status:
                    status = processlist[processes]['status']
                    self.term_window.addnstr(
3520
                        monitor_y + 3 + processes, process_x + 45,
A
asergi 已提交
3521 3522 3523
                        str(status), 1)
                # TIME+
                if tag_proc_time:
3524
                    process_time = processlist[processes]['cpu_times']
3525
                    try:
3526
                        dtime = timedelta(seconds=sum(process_time))
3527
                    except Exception:
3528 3529 3530 3531 3532
                        # Catched on some Amazon EC2 server
                        # See https://github.com/nicolargo/glances/issues/87
                        tag_proc_time = False
                    else:
                        dtime = "{0}:{1}.{2}".format(
A
Alessio Sergi 已提交
3533 3534 3535
                            str(dtime.seconds // 60 % 60),
                            str(dtime.seconds % 60).zfill(2),
                            str(dtime.microseconds)[:2].zfill(2))
3536
                        self.term_window.addnstr(
3537
                            monitor_y + 3 + processes, process_x + 47,
A
asergi 已提交
3538
                            format(dtime, '>8'), 8)
3539
                # IO
3540
                # Hack to allow client 1.6 to connect to server 1.5.2
A
asergi 已提交
3541
                process_tag_io = True
3542
                try:
A
asergi 已提交
3543
                    if processlist[processes]['io_counters'][4] == 0:
3544
                        process_tag_io = True
3545
                except Exception:
3546
                    process_tag_io = False
3547
                if tag_io:
A
asergi 已提交
3548
                    if not process_tag_io:
A
asergi 已提交
3549
                        # If io_tag == 0 (['io_counters'][4])
3550 3551
                        # then do not diplay IO rate
                        self.term_window.addnstr(
3552
                            monitor_y + 3 + processes, process_x + 56,
A
asergi 已提交
3553
                            format("?", '>5'), 5)
3554
                        self.term_window.addnstr(
3555
                            monitor_y + 3 + processes, process_x + 62,
A
asergi 已提交
3556
                            format("?", '>5'), 5)
3557 3558 3559 3560 3561 3562 3563 3564
                    else:
                        # If io_tag == 1 (['io_counters'][4])
                        # then diplay IO rate
                        io_read = processlist[processes]['io_counters'][0]
                        io_read_old = processlist[processes]['io_counters'][2]
                        io_write = processlist[processes]['io_counters'][1]
                        io_write_old = processlist[processes]['io_counters'][3]
                        elapsed_time = max(1, self.__refresh_time)
A
asergi 已提交
3565 3566
                        io_rs = (io_read - io_read_old) / elapsed_time
                        io_ws = (io_write - io_write_old) / elapsed_time
3567
                        self.term_window.addnstr(
3568
                            monitor_y + 3 + processes, process_x + 56,
3569
                            format(self.__autoUnit(io_rs, low_precision=True),
J
Jon Renner 已提交
3570
                                   '>5'), 5)
3571
                        self.term_window.addnstr(
3572
                            monitor_y + 3 + processes, process_x + 62,
3573
                            format(self.__autoUnit(io_ws, low_precision=True),
J
Jon Renner 已提交
3574
                                   '>5'), 5)
3575
                # TCP/UDP connexion number
3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588
                # if tag_tcpudp:
                #     try:
                #         processlist[processes]['tcp']
                #         processlist[processes]['udp']
                #     except:
                #         pass
                #     else:
                #         self.term_window.addnstr(
                #             monitor_y + 3 + processes, process_x + 68,
                #             format(processlist[processes]['tcp'], '>5'), 5)
                #         self.term_window.addnstr(
                #             monitor_y + 3 + processes, process_x + 74,
                #             format(processlist[processes]['udp'], '>5'), 5)
3589
                # Display process command line
A
asergi 已提交
3590
                max_process_name = screen_x - process_x - process_name_x
3591 3592
                process_name = processlist[processes]['name']
                process_cmdline = processlist[processes]['cmdline']
A
asergi 已提交
3593 3594 3595 3596 3597
                if (len(process_cmdline) > max_process_name or
                    len(process_cmdline) == 0):
                    command = process_name
                else:
                    command = process_cmdline
3598
                self.term_window.addnstr(monitor_y + 3 + processes,
A
asergi 已提交
3599 3600 3601
                                         process_x + process_name_x,
                                         command, max_process_name)

A
asergi 已提交
3602
    def displayCaption(self, cs_status="None"):
3603 3604 3605 3606 3607
        """
        Display the caption (bottom left)
        cs_status:
            "None": standalone or server mode
            "Connected": Client is connected to the server
3608
            "Disconnected": Client is disconnected from the server
3609
        """
A
asergi 已提交
3610 3611
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
A
asergi 已提交
3612 3613
        if client_tag:
            if cs_status.lower() == "connected":
3614
                msg_client = _("Connected to ") + format(server_ip)
3615
                msg_client_style = self.default_color2 if self.hascolors else curses.A_UNDERLINE
A
asergi 已提交
3616
            elif cs_status.lower() == "disconnected":
3617
                msg_client = _("Disconnected from ") + format(server_ip)
3618
                msg_client_style = self.ifCRITICAL_color2 if self.hascolors else curses.A_UNDERLINE
3619
        msg_help = _("Press 'h' for help")
A
asergi 已提交
3620
        if client_tag:
3621 3622 3623
            if (screen_y > self.caption_y and
                screen_x > self.caption_x + len(msg_client)):
                self.term_window.addnstr(max(self.caption_y, screen_y - 1),
A
asergi 已提交
3624 3625
                                         self.caption_x, msg_client,
                                         len(msg_client), msg_client_style)
A
asergi 已提交
3626
            if screen_x > self.caption_x + len(msg_client) + 3 + len(msg_help):
3627
                self.term_window.addnstr(max(self.caption_y, screen_y - 1),
A
asergi 已提交
3628 3629
                                         self.caption_x + len(msg_client),
                                         ' | ' + msg_help, 3 + len(msg_help))
3630
        else:
3631 3632 3633 3634
            if (screen_y > self.caption_y and
                screen_x > self.caption_x + len(msg_help)):
                self.term_window.addnstr(max(self.caption_y, screen_y - 1),
                                         self.caption_x, msg_help, len(msg_help))
3635

3636
    def displayHelp(self, core):
A
asergi 已提交
3637 3638 3639 3640 3641 3642 3643
        """
        Show the help panel
        """
        if not self.help_tag:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
A
asergi 已提交
3644
        if screen_y > self.help_y + 23 and screen_x > self.help_x + 79:
A
asergi 已提交
3645 3646 3647
            # Console 80x24 is mandatory to display the help message
            self.erase()

N
Nicolas Hennion 已提交
3648 3649 3650 3651 3652 3653
            try:
                self.term_window.addnstr(
                    self.help_y, self.help_x,
                    _("Glances {0} with PsUtil {1}").format(
                        self.__version, psutil.__version__),
                    79, self.title_color if self.hascolors else 0)
3654
            except Exception:
N
Nicolas Hennion 已提交
3655 3656 3657 3658
                self.term_window.addnstr(
                    self.help_y, self.help_x,
                    _("Glances {0}").format(self.__version),
                    79, self.title_color if self.hascolors else 0)
A
asergi 已提交
3659

A
asergi 已提交
3660
            # display the limits table
3661
            limits_table_x = self.help_x
A
Alessio Sergi 已提交
3662
            limits_table_y = self.help_y + 1
3663
            self.term_window.addnstr(limits_table_y, limits_table_x + 18,
A
asergi 已提交
3664 3665
                                     format(_("OK"), '^8'), 8,
                                     self.default_color)
3666
            self.term_window.addnstr(limits_table_y, limits_table_x + 26,
A
asergi 已提交
3667 3668
                                     format(_("CAREFUL"), '^8'), 8,
                                     self.ifCAREFUL_color),
3669
            self.term_window.addnstr(limits_table_y, limits_table_x + 34,
A
asergi 已提交
3670 3671
                                     format(_("WARNING"), '^8'), 8,
                                     self.ifWARNING_color),
N
Nicolas Hennion 已提交
3672
            self.term_window.addnstr(limits_table_y, limits_table_x + 42,
A
asergi 已提交
3673 3674
                                     format(_("CRITICAL"), '^8'), 8,
                                     self.ifCRITICAL_color),
3675

A
asergi 已提交
3676 3677 3678 3679
            # display the stat labels
            stat_labels = [_("CPU user %"), _("CPU system %"),
                           _("CPU iowait %"), _("Load"),
                           _("RAM memory %"), _("Swap memory %"),
3680 3681 3682
                           _("Temp °C"), _("HDD Temp °C"),
                           _("Filesystem %"), _("CPU process %"),
                           _("MEM process %")]
3683

A
asergi 已提交
3684 3685
            width = 8
            limits_table_x = self.help_x + 2
A
Alessio Sergi 已提交
3686
            limits_table_y = self.help_y + 2
A
asergi 已提交
3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713
            for label in stat_labels:
                self.term_window.addnstr(limits_table_y, limits_table_x,
                                         format(label, '<14'), 14)
                limits_table_y += 1

            # display the limit values
            limit_values = [[0, limits.getCPUCareful(stat='user'),
                             limits.getCPUWarning(stat='user'),
                             limits.getCPUCritical(stat='user')],
                            [0, limits.getCPUCareful(stat='system'),
                             limits.getCPUWarning(stat='system'),
                             limits.getCPUCritical(stat='system')],
                            [0, limits.getCPUCareful(stat='iowait'),
                             limits.getCPUWarning(stat='iowait'),
                             limits.getCPUCritical(stat='iowait')],
                            [0, limits.getLOADCareful() * core,
                             limits.getLOADWarning() * core,
                             limits.getLOADCritical() * core],
                            [0, limits.getMEMCareful(),
                             limits.getMEMWarning(),
                             limits.getMEMCritical()],
                            [0, limits.getSWAPCareful(),
                             limits.getSWAPWarning(),
                             limits.getSWAPCritical()],
                            [0, limits.getTEMPCareful(),
                             limits.getTEMPWarning(),
                             limits.getTEMPCritical()],
3714 3715 3716
                            [0, limits.getHDDTEMPCareful(),
                             limits.getHDDTEMPWarning(),
                             limits.getHDDTEMPCritical()],
A
asergi 已提交
3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727
                            [0, limits.getFSCareful(),
                             limits.getFSWarning(),
                             limits.getFSCritical()],
                            [0, limits.getProcessCareful(stat='CPU', core=core),
                             limits.getProcessWarning(stat='CPU', core=core),
                             limits.getProcessCritical(stat='CPU', core=core)],
                            [0, limits.getProcessCareful(stat='MEM'),
                             limits.getProcessWarning(stat='MEM'),
                             limits.getProcessCritical(stat='MEM')]]

            limits_table_x = self.help_x + 15
A
Alessio Sergi 已提交
3728
            limits_table_y = self.help_y + 2
A
asergi 已提交
3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740
            for value in limit_values:
                self.term_window.addnstr(
                    limits_table_y, limits_table_x,
                    '{0:>{width}}{1:>{width}}{2:>{width}}{3:>{width}}'.format(
                        *value, width=width), 32)
                limits_table_y += 1

            # key table (left column)
            key_col_left = [[_("a"), _("Sort processes automatically")],
                            [_("c"), _("Sort processes by CPU%")],
                            [_("m"), _("Sort processes by MEM%")],
                            [_("p"), _("Sort processes by name")],
A
Alessio Sergi 已提交
3741
                            [_("i"), _("Sort processes by I/O rate")],
A
asergi 已提交
3742 3743 3744 3745
                            [_("d"), _("Show/hide disk I/O stats")],
                            [_("f"), _("Show/hide file system stats")],
                            [_("n"), _("Show/hide network stats")],
                            [_("s"), _("Show/hide sensors stats")],
M
MendelGusmao 已提交
3746
                            [_("y"), _("Show/hide hddtemp stats")]]
A
asergi 已提交
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756

            width = 3
            key_table_x = self.help_x + 2
            key_table_y = limits_table_y + 1
            for key in key_col_left:
                self.term_window.addnstr(
                    key_table_y, key_table_x,
                    '{0:{width}}{1}'.format(*key, width=width), 38)
                key_table_y += 1

3757
                # key table (right column)
A
Alessio Sergi 已提交
3758 3759
                key_col_right = [[_("l"), _("Show/hide logs")],
                                 [_("b"), _("Bytes or bits for network I/O")],
3760 3761 3762
                                 [_("w"), _("Delete warning logs")],
                                 [_("x"), _("Delete warning and critical logs")],
                                 [_("1"), _("Global CPU or per-CPU stats")],
A
Alessio Sergi 已提交
3763 3764 3765
                                 [_("h"), _("Show/hide this help screen")],
                                 [_("t"), _("View network I/O as combination")],
                                 [_("u"), _("View cumulative network I/O")],
3766
                                 [_("q"), _("Quit (Esc and Ctrl-C also work)")]]
3767

A
asergi 已提交
3768 3769 3770 3771 3772 3773 3774
            key_table_x = self.help_x + 38
            key_table_y = limits_table_y + 1
            for key in key_col_right:
                self.term_window.addnstr(
                    key_table_y, key_table_x,
                    '{0:{width}}{1}'.format(*key, width=width), 38)
                key_table_y += 1
A
asergi 已提交
3775

3776 3777
    def displayBat(self, batpercent):
        # Display the current batteries capacities % - Center
A
Alessio Sergi 已提交
3778
        if not batinfo_lib_tag or batpercent == []:
3779 3780 3781 3782 3783 3784
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
        # Build the message to display
        bat_msg = "%d%%" % batpercent
        # Display the message (if possible)
A
Alessio Sergi 已提交
3785 3786
        if (screen_y > self.bat_y and
            screen_x > self.bat_x + len(bat_msg)):
3787 3788 3789 3790 3791 3792
            center = (screen_x // 2) - len(bat_msg) // 2
            self.term_window.addnstr(
                max(self.bat_y, screen_y - 1),
                self.bat_x + center,
                bat_msg, len(bat_msg))

A
asergi 已提交
3793
    def displayNow(self, now):
3794
        # Display the current date and time (now...) - Right
A
asergi 已提交
3795 3796 3797 3798
        if not now:
            return 0
        screen_x = self.screen.getmaxyx()[1]
        screen_y = self.screen.getmaxyx()[0]
A
asergi 已提交
3799
        if screen_y > self.now_y and screen_x > self.now_x:
A
asergi 已提交
3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810
            now_msg = now.strftime(_("%Y-%m-%d %H:%M:%S"))
            self.term_window.addnstr(
                max(self.now_y, screen_y - 1),
                max(self.now_x, screen_x - 1) - len(now_msg),
                now_msg, len(now_msg))


class glancesHtml:
    """
    This class manages the HTML output
    """
3811

A
Alessio Sergi 已提交
3812 3813 3814
    def __init__(self, html_path, refresh_time=1):
        html_filename = 'glances.html'
        html_template = 'default.html'
A
asergi 已提交
3815
        self.__refresh_time = refresh_time
3816

A
Alessio Sergi 已提交
3817 3818 3819
        # Set the HTML output file
        self.html_file = os.path.join(html_path, html_filename)

A
Alessio Sergi 已提交
3820 3821
        # Get data path
        data_path = os.path.join(work_path, 'data')
A
asergi 已提交
3822

A
Alessio Sergi 已提交
3823 3824
        # Set the template path
        template_path = os.path.join(data_path, 'html')
A
asergi 已提交
3825
        environment = jinja2.Environment(
A
Alessio Sergi 已提交
3826
            loader=jinja2.FileSystemLoader(template_path),
A
asergi 已提交
3827 3828 3829
            extensions=['jinja2.ext.loopcontrols'])

        # Open the template
A
Alessio Sergi 已提交
3830
        self.template = environment.get_template(html_template)
A
asergi 已提交
3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845

        # Define the colors list (hash table) for logged stats
        self.__colors_list = {
            'DEFAULT': "bgcdefault fgdefault",
            'OK': "bgcok fgok",
            'CAREFUL': "bgccareful fgcareful",
            'WARNING': "bgcwarning fgcwarning",
            'CRITICAL': "bgcritical fgcritical"
        }

    def __getAlert(self, current=0, max=100):
        # If current < CAREFUL of max then alert = OK
        # If current > CAREFUL of max then alert = CAREFUL
        # If current > WARNING of max then alert = WARNING
        # If current > CRITICAL of max then alert = CRITICAL
N
Nicolas Hennion 已提交
3846
        if max != 0:
A
asergi 已提交
3847
            (current * 100) / max
N
Nicolas Hennion 已提交
3848
        else:
A
asergi 已提交
3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869
            return 'DEFAULT'

        variable = (current * 100) / max

        if variable > limits.getSTDCritical():
            return 'CRITICAL'
        elif variable > limits.getSTDWarning():
            return 'WARNING'
        elif variable > limits.getSTDCareful():
            return 'CAREFUL'

        return 'OK'

    def __getColor(self, current=0, max=100):
        """
        Return colors for logged stats
        """
        return self.__colors_list[self.__getAlert(current, max)]

    def __getCpuColor(self, cpu, max=100):
        cpu['user_color'] = self.__getColor(cpu['user'], max)
A
asergi 已提交
3870
        cpu['system_color'] = self.__getColor(cpu['system'], max)
A
asergi 已提交
3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883
        cpu['nice_color'] = self.__getColor(cpu['nice'], max)
        return cpu

    def __getLoadAlert(self, current=0, core=1):
        # If current < CAREFUL*core of max then alert = OK
        # If current > CAREFUL*core of max then alert = CAREFUL
        # If current > WARNING*core of max then alert = WARNING
        # If current > CRITICAL*core of max then alert = CRITICAL
        if current > limits.getLOADCritical(core):
            return 'CRITICAL'
        elif current > limits.getLOADWarning(core):
            return 'WARNING'
        elif current > limits.getLOADCareful(core):
3884
            return 'CAREFUL'
A
asergi 已提交
3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896
        return 'OK'

    def __getLoadColor(self, load, core=1):
        load['min1_color'] = (
            self.__colors_list[self.__getLoadAlert(load['min1'], core)])
        load['min5_color'] = (
            self.__colors_list[self.__getLoadAlert(load['min5'], core)])
        load['min15_color'] = (
            self.__colors_list[self.__getLoadAlert(load['min15'], core)])
        return load

    def __getMemColor(self, mem):
A
asergi 已提交
3897
        mem['used_color'] = self.__getColor(mem['used'], mem['total'])
A
asergi 已提交
3898 3899 3900 3901 3902 3903 3904 3905 3906 3907

        return mem

    def __getMemSwapColor(self, memswap):
        memswap['used_color'] = self.__getColor(memswap['used'],
                                                memswap['total'])
        return memswap

    def __getFsColor(self, fs):
        mounted = 0
3908
        for mounted in range(0, len(fs)):
A
asergi 已提交
3909 3910 3911 3912 3913 3914 3915
            fs[mounted]['used_color'] = self.__getColor(fs[mounted]['used'],
                                                        fs[mounted]['size'])
        return fs

    def update(self, stats):
        if stats.getCpu():
            # Open the output file
A
Alessio Sergi 已提交
3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935
            with open(self.html_file, 'w') as f:
                # HTML refresh is set to 1.5 * refresh_time
                # to avoid display while page rendering
                data = self.template.render(
                    refresh=int(self.__refresh_time * 1.5),
                    host=stats.getHost(),
                    system=stats.getSystem(),
                    cpu=self.__getCpuColor(stats.getCpu()),
                    load=self.__getLoadColor(stats.getLoad(), stats.getCore()),
                    core=stats.getCore(),
                    mem=self.__getMemColor(stats.getMem()),
                    memswap=self.__getMemSwapColor(stats.getMemSwap()),
                    net=stats.getNetwork(),
                    diskio=stats.getDiskIO(),
                    fs=self.__getFsColor(stats.getFs()),
                    proccount=stats.getProcessCount(),
                    proclist=stats.getProcessList())

                # Write data into the file
                f.write(data)
A
asergi 已提交
3936 3937 3938 3939


class glancesCsv:
    """
3940
    This class manages the CSV output
A
asergi 已提交
3941
    """
3942

A
asergi 已提交
3943 3944 3945 3946 3947 3948 3949 3950
    def __init__(self, cvsfile="./glances.csv", refresh_time=1):
        # Init refresh time
        self.__refresh_time = refresh_time

        # Set the ouput (CSV) path
        try:
            self.__cvsfile_fd = open("%s" % cvsfile, "wb")
            self.__csvfile = csv.writer(self.__cvsfile_fd)
3951
        except IOError as error:
A
Alessio Sergi 已提交
3952
            print("Cannot create the output CSV file: ", error[1])
A
asergi 已提交
3953 3954 3955 3956 3957 3958 3959 3960 3961
            sys.exit(0)

    def exit(self):
        self.__cvsfile_fd.close()

    def update(self, stats):
        if stats.getCpu():
            # Update CSV with the CPU stats
            cpu = stats.getCpu()
A
asergi 已提交
3962
            self.__csvfile.writerow(["cpu", cpu['user'], cpu['system'],
A
asergi 已提交
3963 3964 3965 3966 3967 3968
                                     cpu['nice']])
        if stats.getLoad():
            # Update CSV with the LOAD stats
            load = stats.getLoad()
            self.__csvfile.writerow(["load", load['min1'], load['min5'],
                                     load['min15']])
A
asergi 已提交
3969
        if stats.getMem() and stats.getMemSwap():
A
asergi 已提交
3970 3971 3972 3973 3974 3975 3976 3977 3978
            # Update CSV with the MEM stats
            mem = stats.getMem()
            self.__csvfile.writerow(["mem", mem['total'], mem['used'],
                                     mem['free']])
            memswap = stats.getMemSwap()
            self.__csvfile.writerow(["swap", memswap['total'], memswap['used'],
                                     memswap['free']])
        self.__cvsfile_fd.flush()

3979

A
Alessio Sergi 已提交
3980
class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler):
3981
    """
3982
    Main XMLRPC handler
3983
    """
3984
    rpc_paths = ('/RPC2', )
3985

3986 3987 3988 3989 3990
    def end_headers(self):
        # Hack to add a specific header
        # Thk to: https://gist.github.com/rca/4063325
        self.send_my_headers()
        SimpleXMLRPCRequestHandler.end_headers(self)
N
Nicolas Hennion 已提交
3991

3992 3993 3994
    def send_my_headers(self):
        # Specific header is here (solved the issue #227)
        self.send_header("Access-Control-Allow-Origin", "*")
N
Nicolas Hennion 已提交
3995

3996
    def authenticate(self, headers):
A
asergi 已提交
3997
        # auth = headers.get('Authorization')
3998 3999
        try:
            (basic, _, encoded) = headers.get('Authorization').partition(' ')
4000
        except Exception:
4001
            # Client did not ask for authentidaction
A
asergi 已提交
4002
            # If server need it then exit
4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022
            return not self.server.isAuth
        else:
            # Client authentication
            (basic, _, encoded) = headers.get('Authorization').partition(' ')
            assert basic == 'Basic', 'Only basic authentication supported'
            #    Encoded portion of the header is a string
            #    Need to convert to bytestring
            encodedByteString = encoded.encode()
            #    Decode Base64 byte String to a decoded Byte String
            decodedBytes = b64decode(encodedByteString)
            #    Convert from byte string to a regular String
            decodedString = decodedBytes.decode()
            #    Get the username and password from the string
            (username, _, password) = decodedString.partition(':')
            #    Check that username and password match internal global dictionary
            return self.check_user(username, password)

    def check_user(self, username, password):
        # Check username and password in the dictionnary
        if username in self.server.user_dict:
A
asergi 已提交
4023
            if self.server.user_dict[username] == md5(password).hexdigest():
4024 4025 4026
                return True
        return False

A
asergi 已提交
4027
    def parse_request(self):
4028 4029 4030 4031 4032 4033 4034 4035 4036
        if SimpleXMLRPCRequestHandler.parse_request(self):
            # Next we authenticate
            if self.authenticate(self.headers):
                return True
            else:
                # if authentication fails, tell the client
                self.send_error(401, 'Authentication failed')
        return False

N
Nicolas Hennion 已提交
4037 4038 4039
    def log_message(self, format, *args):
        # No message displayed on the server side
        pass
4040

4041

A
Alessio Sergi 已提交
4042 4043 4044 4045
class GlancesXMLRPCServer(SimpleXMLRPCServer):
    """
    Init a SimpleXMLRPCServer instance (IPv6-ready)
    """
4046

A
Alessio Sergi 已提交
4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059
    def __init__(self, bind_address, bind_port=61209,
                 requestHandler=GlancesXMLRPCHandler):

        try:
            self.address_family = socket.getaddrinfo(bind_address, bind_port)[0][0]
        except socket.error as e:
            print(_("Couldn't open socket: %s") % e)
            sys.exit(1)

        SimpleXMLRPCServer.__init__(self, (bind_address, bind_port),
                                    requestHandler)


4060 4061 4062
class GlancesInstance():
    """
    All the methods of this class are published as XML RPC methods
4063
    """
4064

4065
    def __init__(self, cached_time=1):
J
Jon Renner 已提交
4066 4067 4068
        # cached_time is the minimum time interval between stats updates
        # i.e. XML/RPC calls will not retrieve updated info until the time
        # since last update is passed (will retrieve old cached info instead)
N
Nicolas Hennion 已提交
4069
        self.timer = Timer(0)
4070
        self.cached_time = cached_time
4071

N
Nicolas Hennion 已提交
4072
    def __update__(self):
4073
        # Never update more than 1 time per cached_time
N
Nicolas Hennion 已提交
4074
        if self.timer.finished():
4075
            stats.update()
J
Jon Renner 已提交
4076
            self.timer = Timer(self.cached_time)
4077

4078
    def init(self):
4079 4080 4081
        # Return the Glances version
        return __version__

N
Nicolas Hennion 已提交
4082
    def getAll(self):
4083
        # Update and return all the stats
N
Nicolas Hennion 已提交
4084
        self.__update__()
4085
        return json.dumps(stats.getAll())
4086

4087 4088 4089 4090
    def getAllLimits(self):
        # Return all the limits
        return json.dumps(limits.getAll())

4091 4092 4093 4094
    def getAllMonitored(self):
        # Return the processes monitored list
        return json.dumps(monitors.getAll())

4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105
    def getSystem(self):
        # Return operating system info
        # No need to update...
        #~ self.__update__()
        return json.dumps(stats.getSystem())

    def getCore(self):
        # Update and return number of Core
        self.__update__()
        return json.dumps(stats.getCore())

N
Nicolas Hennion 已提交
4106 4107
    def getCpu(self):
        # Update and return CPU stats
N
Nicolas Hennion 已提交
4108
        self.__update__()
N
Nicolas Hennion 已提交
4109 4110 4111 4112
        return json.dumps(stats.getCpu())

    def getLoad(self):
        # Update and return LOAD stats
N
Nicolas Hennion 已提交
4113
        self.__update__()
N
Nicolas Hennion 已提交
4114 4115 4116 4117
        return json.dumps(stats.getLoad())

    def getMem(self):
        # Update and return MEM stats
N
Nicolas Hennion 已提交
4118
        self.__update__()
N
Nicolas Hennion 已提交
4119 4120 4121 4122
        return json.dumps(stats.getMem())

    def getMemSwap(self):
        # Update and return MEMSWAP stats
N
Nicolas Hennion 已提交
4123
        self.__update__()
N
Nicolas Hennion 已提交
4124
        return json.dumps(stats.getMemSwap())
A
asergi 已提交
4125

4126 4127 4128
    def getSensors(self):
        # Update and return SENSORS stats
        self.__update__()
A
asergi 已提交
4129
        return json.dumps(stats.getSensors())
4130

M
MendelGusmao 已提交
4131 4132 4133 4134 4135
    def getHDDTemp(self):
        # Update and return HDDTEMP stats
        self.__update__()
        return json.dumps(stats.getHDDTemp())

4136 4137 4138
    def getNetwork(self):
        # Update and return NET stats
        self.__update__()
A
asergi 已提交
4139
        return json.dumps(stats.getNetwork())
4140 4141 4142 4143

    def getDiskIO(self):
        # Update and return DISK IO stats
        self.__update__()
A
asergi 已提交
4144
        return json.dumps(stats.getDiskIO())
4145 4146 4147 4148

    def getFs(self):
        # Update and return FS stats
        self.__update__()
A
asergi 已提交
4149
        return json.dumps(stats.getFs())
4150 4151 4152 4153

    def getProcessCount(self):
        # Update and return ProcessCount stats
        self.__update__()
A
asergi 已提交
4154
        return json.dumps(stats.getProcessCount())
4155 4156 4157 4158

    def getProcessList(self):
        # Update and return ProcessList stats
        self.__update__()
A
asergi 已提交
4159
        return json.dumps(stats.getProcessList())
N
Nicolas Hennion 已提交
4160

4161 4162 4163 4164 4165
    def getBatPercent(self):
        # Update and return total batteries percent stats
        self.__update__()
        return json.dumps(stats.getBatPercent())

N
Nicolas Hennion 已提交
4166 4167 4168
    def getNow(self):
        # Update and return current date/hour
        self.__update__()
4169
        return json.dumps(stats.getNow().strftime(_("%Y-%m-%d %H:%M:%S")))
N
Nicolas Hennion 已提交
4170 4171 4172 4173 4174 4175

    def __getTimeSinceLastUpdate(self, IOType):
        assert(IOType in ['net', 'disk', 'process_disk'])
        return getTimeSinceLastUpdate(IOType)

    def getNetTimeSinceLastUpdate(self):
N
Nicolas Hennion 已提交
4176
        return getTimeSinceLastUpdate('net')
N
Nicolas Hennion 已提交
4177 4178 4179 4180 4181 4182 4183

    def getDiskTimeSinceLastUpdate(self):
        return getTimeSinceLastUpdate('net')

    def getProcessDiskTimeSinceLastUpdate(self):
        return getTimeSinceLastUpdate('process_disk')

4184

4185
class GlancesServer():
4186 4187
    """
    This class creates and manages the TCP client
4188
    """
4189

A
asergi 已提交
4190
    def __init__(self, bind_address, bind_port=61209,
A
Alessio Sergi 已提交
4191
                 requestHandler=GlancesXMLRPCHandler, cached_time=1):
4192
        self.server = GlancesXMLRPCServer(bind_address, bind_port, requestHandler)
A
Alessio Sergi 已提交
4193

4194 4195 4196 4197 4198 4199
        # The users dict
        # username / MD5 password couple
        # By default, no auth is needed
        self.server.user_dict = {}
        self.server.isAuth = False
        # Register functions
4200
        self.server.register_introspection_functions()
4201
        self.server.register_instance(GlancesInstance(cached_time))
4202 4203

    def add_user(self, username, password):
4204
        """
A
asergi 已提交
4205
        Add an user to the dictionnary
4206
        """
4207 4208
        self.server.user_dict[username] = md5(password).hexdigest()
        self.server.isAuth = True
4209

4210 4211
    def serve_forever(self):
        self.server.serve_forever()
4212

4213 4214
    def server_close(self):
        self.server.server_close()
4215

4216

4217 4218 4219
class GlancesClient():
    """
    This class creates and manages the TCP client
A
asergi 已提交
4220
    """
4221

A
asergi 已提交
4222 4223
    def __init__(self, server_address, server_port=61209,
                 username="glances", password=""):
4224
        # Build the URI
A
asergi 已提交
4225
        if password != "":
4226 4227 4228
            uri = 'http://%s:%s@%s:%d' % (username, password, server_address, server_port)
        else:
            uri = 'http://%s:%d' % (server_address, server_port)
A
asergi 已提交
4229

4230
        # Try to connect to the URI
N
Nicolas Hennion 已提交
4231
        try:
4232
            self.client = ServerProxy(uri)
4233
        except Exception:
4234
            print(_("Error: creating client socket") + " %s" % uri)
N
Nicolas Hennion 已提交
4235
            pass
4236
        return
A
asergi 已提交
4237

4238
    def client_init(self):
4239
        try:
D
dongweiming 已提交
4240
            client_version = self.client.init()
A
asergi 已提交
4241 4242
        except ProtocolError as err:
            if str(err).find(" 401 ") > 0:
4243 4244 4245 4246
                print(_("Error: Connection to server failed. Bad password."))
                sys.exit(-1)
            else:
                print(_("Error: Connection to server failed. Unknown error."))
A
asergi 已提交
4247
                sys.exit(-1)
D
dongweiming 已提交
4248
        return __version__[:3] == client_version[:3]
4249 4250 4251 4252

    def client_get_limits(self):
        try:
            serverlimits = json.loads(self.client.getAllLimits())
4253
        except Exception:
4254 4255 4256
            return {}
        else:
            return serverlimits
4257

4258 4259 4260
    def client_get_monitored(self):
        try:
            servermonitored = json.loads(self.client.getAllMonitored())
4261
        except Exception:
4262 4263 4264 4265
            return []
        else:
            return servermonitored

4266
    def client_get(self):
N
Nicolas Hennion 已提交
4267 4268
        try:
            stats = json.loads(self.client.getAll())
4269
        except Exception:
N
Nicolas Hennion 已提交
4270 4271
            return {}
        else:
A
asergi 已提交
4272
            return stats
4273

A
asergi 已提交
4274 4275 4276
# Global def
#===========

A
Alessio Sergi 已提交
4277

A
asergi 已提交
4278
def printVersion():
4279
    print(_("Glances version ") + __version__ + _(" with PsUtil ") + psutil.__version__)
A
asergi 已提交
4280 4281 4282 4283


def printSyntax():
    printVersion()
A
Alessio Sergi 已提交
4284 4285
    print(_("Usage: glances [options]"))
    print(_("\nOptions:"))
4286
    print(_("\t-b\t\tDisplay network rate in Byte per second"))
A
Alessio Sergi 已提交
4287 4288 4289
    print(_("\t-B @IP|HOST\tBind server to the given IPv4/IPv6 address or hostname"))
    print(_("\t-c @IP|HOST\tConnect to a Glances server by IPv4/IPv6 address or hostname"))
    print(_("\t-C FILE\t\tPath to the configuration file"))
4290
    print(_("\t-d\t\tDisable disk I/O module"))
A
Alessio Sergi 已提交
4291
    print(_("\t-e\t\tEnable sensors module"))
A
Alessio Sergi 已提交
4292
    print(_("\t-f FILE\t\tSet the HTML output folder or CSV file"))
A
Alessio Sergi 已提交
4293
    print(_("\t-h\t\tDisplay the help and exit"))
4294 4295
    print(_("\t-m\t\tDisable mount module"))
    print(_("\t-n\t\tDisable network module"))
A
Alessio Sergi 已提交
4296
    print(_("\t-o OUTPUT\tDefine additional output (available: HTML or CSV)"))
A
Alessio Sergi 已提交
4297
    print(_("\t-p PORT\t\tDefine the client/server TCP port (default: %d)" %
4298
            server_port))
A
Alessio Sergi 已提交
4299 4300
    print(_("\t-P PASSWORD\tDefine a client/server password"))
    print(_("\t--password\tDefine a client/server password from the prompt"))
A
Alessio Sergi 已提交
4301
    print(_("\t-r\t\tDisable process list"))
4302
    print(_("\t-s\t\tRun Glances in server mode"))
A
Alessio Sergi 已提交
4303
    print(_("\t-t SECONDS\tSet refresh time in seconds (default: %d sec)" %
4304 4305
            refresh_time))
    print(_("\t-v\t\tDisplay the version and exit"))
A
Alessio Sergi 已提交
4306
    print(_("\t-y\t\tEnable hddtemp module"))
4307
    print(_("\t-z\t\tDo not use the bold color attribute"))
4308
    print(_("\t-1\t\tStart Glances in per CPU mode"))
A
asergi 已提交
4309 4310


4311 4312 4313 4314 4315 4316 4317
def end():
    if server_tag:
        # Stop the server loop
        server.server_close()
    else:
        if client_tag:
            # Stop the client loop
4318 4319
            #~ client.client_quit()
            pass
4320

4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333
        # Stop the classical CLI loop
        screen.end()

        if csv_tag:
            csvoutput.exit()

    sys.exit(0)


def signal_handler(signal, frame):
    end()


4334
def getpassword(description='', confirm=False):
4335 4336 4337 4338 4339
    """
    Read a password from the command line (with confirmation if confirm = True)
    """
    import getpass

4340
    if description != '':
4341
        sys.stdout.write("%s\n" % description)
4342 4343 4344

    password1 = getpass.getpass(_("Password: "))
    if confirm:
4345 4346 4347 4348
        password2 = getpass.getpass(_("Password (confirm): "))
    else:
        return password1

4349
    if password1 == password2:
4350 4351 4352
        return password1
    else:
        sys.stdout.write(_("[Warning] Passwords did not match, please try again...\n"))
4353
        return getpassword(description=description, confirm=confirm)
4354 4355


4356
def main():
4357 4358 4359
    # Glances - Init stuff
    ######################

4360
    global config, limits, monitors, logs, stats, screen
A
asergi 已提交
4361
    global htmloutput, csvoutput
4362
    global html_tag, csv_tag, server_tag, client_tag
A
asergi 已提交
4363
    global psutil_get_io_counter_tag, psutil_mem_vm
4364 4365
    global percpu_tag, fs_tag, diskio_tag, network_tag, network_bytepersec_tag
    global sensors_tag, hddtemp_tag, process_tag
4366
    global refresh_time, client, server, server_port, server_ip
4367 4368 4369 4370
    global last_update_times

    # create update times dict
    last_update_times = {}
A
asergi 已提交
4371 4372

    # Set default tags
4373
    percpu_tag = False
A
asergi 已提交
4374 4375 4376
    fs_tag = True
    diskio_tag = True
    network_tag = True
4377
    network_bytepersec_tag = False
N
Nicolas Hennion 已提交
4378
    sensors_tag = False
M
MendelGusmao 已提交
4379
    hddtemp_tag = False
4380
    process_tag = True
A
asergi 已提交
4381 4382
    html_tag = False
    csv_tag = False
4383
    client_tag = False
A
Alessio Sergi 已提交
4384 4385
    if is_Windows and not is_colorConsole:
        # Force server mode for Windows OS without colorconsole
N
Nicolas Hennion 已提交
4386 4387 4388
        server_tag = True
    else:
        server_tag = False
4389

A
asergi 已提交
4390 4391
    # Configuration file stuff
    conf_file = ""
4392
    conf_file_tag = False
4393

A
asergi 已提交
4394
    # Set the default refresh time
4395
    refresh_time = 3
4396

4397 4398 4399
    # Set the default cache lifetime (for server)
    cached_time = 1

4400 4401 4402
    # Use curses.A_BOLD by default
    use_bold = True

4403 4404
    # Set the default TCP port for client and server
    server_port = 61209
4405
    bind_ip = "0.0.0.0"
A
asergi 已提交
4406

4407 4408 4409 4410
    # Default username/password
    username = "glances"
    password = ""

A
asergi 已提交
4411 4412
    # Manage args
    try:
4413
        opts, args = getopt.getopt(sys.argv[1:], "B:bdeymnho:f:t:vsc:p:C:P:zr1",
4414
                                   ["bind", "bytepersec", "diskio", "mount",
M
MendelGusmao 已提交
4415
                                    "sensors", "hddtemp", "netrate", "help", "output",
N
Nicolas Hennion 已提交
4416
                                    "file", "time", "version", "server",
4417
                                    "client", "port", "config", "password",
4418
                                    "nobold", "noproc", "percpu"])
4419
    except getopt.GetoptError as err:
A
asergi 已提交
4420
        # Print help information and exit:
4421
        print(str(err))
A
asergi 已提交
4422 4423 4424 4425 4426 4427
        printSyntax()
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-v", "--version"):
            printVersion()
            sys.exit(0)
4428 4429
        elif opt in ("-s", "--server"):
            server_tag = True
4430 4431 4432 4433 4434 4435 4436
        elif opt in ("-P", "--password"):
            try:
                arg
            except NameError:
                print(_("Error: -P flag need an argument (password)"))
                sys.exit(2)
            password = arg
4437 4438 4439 4440 4441 4442 4443
        elif opt in ("-B", "--bind"):
            try:
                arg
            except NameError:
                print(_("Error: -B flag need an argument (bind IP address)"))
                sys.exit(2)
            bind_ip = arg
4444 4445 4446 4447 4448
        elif opt in ("-c", "--client"):
            client_tag = True
            try:
                arg
            except NameError:
4449
                print(_("Error: -c flag need an argument (server IP address/name)"))
4450 4451 4452
                sys.exit(2)
            server_ip = arg
        elif opt in ("-p", "--port"):
4453 4454 4455 4456 4457
            try:
                port_number = int(arg)
            except:
                print("invalid port number argument: %s" % arg)
                sys.exit(2)
4458
            server_port = arg
A
asergi 已提交
4459
        elif opt in ("-o", "--output"):
N
Nicolas Hennion 已提交
4460
            if arg.lower() == "html":
N
Nicolas Hennion 已提交
4461
                html_tag = True
4462
            elif arg.lower() == "csv":
N
Nicolas Hennion 已提交
4463
                csv_tag = True
A
asergi 已提交
4464
            else:
4465
                print(_("Error: Unknown output %s" % arg))
N
Nicolas Hennion 已提交
4466
                printSyntax()
A
asergi 已提交
4467
                sys.exit(2)
N
Nicolas Hennion 已提交
4468
        elif opt in ("-e", "--sensors"):
A
asergi 已提交
4469 4470 4471 4472 4473 4474
            if is_Linux:
                if not sensors_lib_tag:
                    print(_("Error: PySensors library not found"))
                    sys.exit(2)
                else:
                    sensors_tag = True
N
Nicolas Hennion 已提交
4475
            else:
A
asergi 已提交
4476
                print(_("Error: Sensors module is only available on Linux"))
M
MendelGusmao 已提交
4477 4478
        elif opt in ("-y", "--hddtemp"):
            hddtemp_tag = True
A
asergi 已提交
4479 4480 4481 4482 4483 4484 4485
        elif opt in ("-f", "--file"):
            output_file = arg
            output_folder = arg
        elif opt in ("-t", "--time"):
            if int(arg) >= 1:
                refresh_time = int(arg)
            else:
4486
                print(_("Error: Refresh time should be a positive integer"))
A
asergi 已提交
4487 4488
                sys.exit(2)
        elif opt in ("-d", "--diskio"):
A
asergi 已提交
4489
            diskio_tag = False
A
asergi 已提交
4490
        elif opt in ("-m", "--mount"):
A
asergi 已提交
4491
            fs_tag = False
A
asergi 已提交
4492
        elif opt in ("-n", "--netrate"):
A
asergi 已提交
4493
            network_tag = False
4494 4495
        elif opt in ("-b", "--bytepersec"):
            network_bytepersec_tag = True
4496 4497
        elif opt in ("-C", "--config"):
            conf_file = arg
4498
            conf_file_tag = True
4499 4500
        elif opt in ("-z", "--nobold"):
            use_bold = False
4501 4502
        elif opt in ("-r", "--noproc"):
            process_tag = False
4503 4504
        elif opt in ("-1", "--percpu"):
            percpu_tag = True
A
asergi 已提交
4505 4506 4507 4508 4509
        else:
            printSyntax()
            sys.exit(0)

    # Check options
4510 4511
    if server_tag:
        if client_tag:
A
Alessio Sergi 已提交
4512
            print(_("Error: Cannot use both -s and -c flag"))
4513 4514
            sys.exit(2)
        if html_tag or csv_tag:
A
Alessio Sergi 已提交
4515
            print(_("Error: Cannot use both -s and -o flag"))
4516
            sys.exit(2)
4517 4518
        if password == '':
            password = getpassword(description=_("Define the password for the Glances server"), confirm=True)
4519 4520 4521

    if client_tag:
        if html_tag or csv_tag:
A
Alessio Sergi 已提交
4522
            print(_("Error: Cannot use both -c and -o flag"))
4523
            sys.exit(2)
4524
        if conf_file_tag:
A
Alessio Sergi 已提交
4525
            print(_("Error: Cannot use both -c and -C flag"))
4526
            print(_("       Limits are set based on the server ones"))
A
asergi 已提交
4527
            sys.exit(2)
4528 4529
        if password == '':
            password = getpassword(description=_("Enter the Glances server password"), confirm=False)
4530

A
asergi 已提交
4531
    if html_tag:
N
Nicolas Hennion 已提交
4532 4533 4534 4535
        if not html_lib_tag:
            print(_("Error: Need Jinja2 library to export into HTML"))
            print(_("Try to install the python-jinja2 package"))
            sys.exit(2)
A
asergi 已提交
4536 4537 4538
        try:
            output_folder
        except UnboundLocalError:
A
Alessio Sergi 已提交
4539
            print(_("Error: HTML export (-o html) need "
4540
                    "output folder definition (-f <folder>)"))
A
asergi 已提交
4541 4542 4543
            sys.exit(2)

    if csv_tag:
N
Nicolas Hennion 已提交
4544 4545 4546
        if not csv_lib_tag:
            print(_("Error: Need CSV library to export into CSV"))
            sys.exit(2)
A
asergi 已提交
4547 4548 4549
        try:
            output_file
        except UnboundLocalError:
4550 4551
            print(_("Error: CSV export (-o csv) need "
                    "output file definition (-f <file>)"))
A
asergi 已提交
4552 4553 4554 4555 4556
            sys.exit(2)

    # Catch CTRL-C
    signal.signal(signal.SIGINT, signal_handler)

A
asergi 已提交
4557 4558 4559 4560 4561
    if conf_file_tag:
        config = Config(conf_file)
    else:
        config = Config()

4562 4563
    if client_tag:
        psutil_get_io_counter_tag = True
4564
        psutil_mem_vm = False
A
asergi 已提交
4565 4566 4567
        fs_tag = True
        diskio_tag = True
        network_tag = True
4568
        sensors_tag = True
M
MendelGusmao 已提交
4569
        hddtemp_tag = True
N
Nicolas Hennion 已提交
4570 4571
    elif server_tag:
        sensors_tag = True
M
MendelGusmao 已提交
4572
        hddtemp_tag = True
4573 4574

    # Init Glances depending of the mode (standalone, client, server)
4575
    if server_tag:
4576
        # Init the server
A
asergi 已提交
4577
        print(_("Glances server is running on") + " %s:%s" % (bind_ip, server_port))
A
Alessio Sergi 已提交
4578
        server = GlancesServer(bind_ip, int(server_port), GlancesXMLRPCHandler, cached_time)
4579

4580
        # Set the server login/password (if -P tag)
A
asergi 已提交
4581
        if password != "":
4582 4583
            server.add_user(username, password)

4584
        # Init Limits
A
asergi 已提交
4585
        limits = glancesLimits()
4586

4587 4588 4589
        # Init monitor list
        monitors = monitorList()

4590
        # Init stats
4591 4592
        stats = GlancesStatsServer()
        stats.update({})
4593 4594
    elif client_tag:
        # Init the client (displaying server stat in the CLI)
4595
        client = GlancesClient(server_ip, int(server_port), username, password)
4596 4597 4598

        # Test if client and server are in the same major version
        if not client.client_init():
4599 4600
            print(_("Error: The server version is not compatible"))
            sys.exit(2)
4601

4602
        # Init Limits
A
asergi 已提交
4603
        limits = glancesLimits()
A
asergi 已提交
4604

4605 4606 4607
        # Init monitor list
        monitors = monitorList()

4608 4609
        # Init Logs
        logs = glancesLogs()
A
asergi 已提交
4610

4611
        # Init stats
4612
        stats = GlancesStatsClient()
A
asergi 已提交
4613

4614
        # Init screen
4615
        screen = glancesScreen(refresh_time=refresh_time,
4616
                               use_bold=use_bold)
4617 4618
    else:
        # Init the classical CLI
4619

4620
        # Init Limits
A
asergi 已提交
4621
        limits = glancesLimits()
A
asergi 已提交
4622

4623 4624 4625
        # Init monitor list
        monitors = monitorList()

4626 4627
        # Init Logs
        logs = glancesLogs()
A
asergi 已提交
4628

4629
        # Init stats
4630
        stats = GlancesStats()
4631 4632 4633

        # Init HTML output
        if html_tag:
A
Alessio Sergi 已提交
4634
            htmloutput = glancesHtml(html_path=output_folder,
4635 4636 4637 4638 4639 4640 4641 4642
                                     refresh_time=refresh_time)

        # Init CSV output
        if csv_tag:
            csvoutput = glancesCsv(cvsfile=output_file,
                                   refresh_time=refresh_time)

        # Init screen
4643
        screen = glancesScreen(refresh_time=refresh_time,
4644
                               use_bold=use_bold)
A
asergi 已提交
4645

4646 4647
    # Glances - Main loop
    #####################
4648

4649 4650 4651 4652
    if server_tag:
        # Start the server loop
        server.serve_forever()
    elif client_tag:
4653 4654
        # Set the limits to the server ones
        server_limits = client.client_get_limits()
A
asergi 已提交
4655
        if server_limits != {}:
4656
            limits.setAll(server_limits)
A
asergi 已提交
4657

4658 4659 4660 4661 4662
        # Set the monitored pocesses list to the server one
        server_monitored = client.client_get_monitored()
        if server_monitored != []:
            monitors.setAll(server_monitored)

4663
        # Start the client (CLI) loop
4664
        while True:
4665 4666 4667 4668 4669 4670 4671
            # Get server system informations
            server_stats = client.client_get()
            if server_stats == {}:
                server_status = "Disconnected"
            else:
                server_status = "Connected"
                stats.update(server_stats)
4672
            # Update the screen
A
asergi 已提交
4673
            screen.update(stats, cs_status=server_status)
4674
    else:
4675
        # Start the standalone (CLI) loop
4676 4677 4678
        while True:
            # Get system informations
            stats.update()
A
asergi 已提交
4679

4680 4681
            # Update the screen
            screen.update(stats)
A
asergi 已提交
4682

4683 4684 4685
            # Update the HTML output
            if html_tag:
                htmloutput.update(stats)
A
asergi 已提交
4686

4687 4688 4689
            # Update the CSV output
            if csv_tag:
                csvoutput.update(stats)
A
asergi 已提交
4690

4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703

def getTimeSinceLastUpdate(IOType):
    global last_update_times
    assert(IOType in ['net', 'disk', 'process_disk'])
    current_time = time.time()
    last_time = last_update_times.get(IOType)
    if not last_time:
        time_since_update = 1
    else:
        time_since_update = current_time - last_time
    last_update_times[IOType] = current_time
    return time_since_update

N
Nicolas Hennion 已提交
4704 4705 4706 4707
# Main
#=====

if __name__ == "__main__":
4708
    main()
N
Nicolas Hennion 已提交
4709 4710


A
asergi 已提交
4711
# The end...