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

"""Glances main class."""
N
Nicolas Hennion 已提交
21

N
Nicolas Hennion 已提交
22
import argparse
23
import os
24
import sys
25
import tempfile
N
Nicolas Hennion 已提交
26

A
Alessio Sergi 已提交
27
from glances import __version__, psutil_version
N
nicolargo 已提交
28
from glances.compat import input
29
from glances.config import Config
30
from glances.globals import LINUX, WINDOWS
31
from glances.logger import logger
N
Nicolas Hennion 已提交
32

N
Nicolas Hennion 已提交
33

34 35 36 37 38 39 40 41 42 43
def disable(class_name, var):
    """Set disable_<var> to True in the class class_name."""
    setattr(class_name, 'disable_' + var, True)


def enable(class_name, var):
    """Set disable_<var> to False in the class class_name."""
    setattr(class_name, 'disable_' + var, False)


44
class GlancesMain(object):
A
PEP 257  
Alessio Sergi 已提交
45
    """Main class to manage Glances instance."""
N
Nicolas Hennion 已提交
46

N
Nicolas Hennion 已提交
47 48
    # Default stats' refresh time is 3 seconds
    refresh_time = 3
N
Nicolas Hennion 已提交
49
    # Set the default cache lifetime to 1 second (only for server)
A
Alessio Sergi 已提交
50
    cached_time = 1
N
Nicolas Hennion 已提交
51 52 53 54
    # By default, Glances is ran in standalone mode (no client/server)
    client_tag = False
    # Server TCP port number (default is 61209)
    server_port = 61209
N
Nicolas Hennion 已提交
55 56
    # Web Server TCP port number (default is 61208)
    web_server_port = 61208
N
Nicolas Hennion 已提交
57 58 59 60
    # Default username/password for client/server mode
    username = "glances"
    password = ""

61 62 63 64 65 66
    # Examples of use
    example_of_use = """
Examples of use:
  Monitor local machine (standalone mode):
    $ glances

67 68 69
  Display all Glances modules (plugins and exporters) and exit:
    $ glances --module-list

70
  Monitor local machine with the Web interface and start Restful server:
71 72 73
    $ glances -w
    Glances web server started on http://0.0.0.0:61208/

74 75 76 77
  Only start Restful API (without the WebUI):
    $ glances -w --disable-webui
    Glances API available on http://0.0.0.0:61208/api/

78
  Monitor local machine and export stats to a CSV file (standalone mode):
79
    $ glances --export csv --export-csv-file /tmp/glances.csv
80 81

  Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode):
82
    $ glances -t 5 --export influxdb
83

84
  Start a Glances XML/RCP server (server mode):
85 86
    $ glances -s

87
  Connect Glances to a Glances XML/RCP server (client mode):
88 89 90
    $ glances -c <ip_server>

  Connect Glances to a Glances server and export stats to a StatsD server (client mode):
91
    $ glances -c <ip_server> --export statsd
92 93 94

  Start the client browser (browser mode):
    $ glances --browser
95 96 97

  Disable some plugins (any modes):
    $ glances --disable-plugin network,ports
98
"""
N
Nicolargo 已提交
99

N
Nicolas Hennion 已提交
100
    def __init__(self):
A
Alessio Sergi 已提交
101
        """Manage the command line arguments."""
102
        # Read the command line arguments
A
Alessio Sergi 已提交
103 104 105 106
        self.args = self.parse_args()

    def init_args(self):
        """Init all the command line arguments."""
107
        version = "Glances v" + __version__ + " with psutil v" + psutil_version
108
        parser = argparse.ArgumentParser(
A
Alessio Sergi 已提交
109
            prog='glances',
N
Nicolargo 已提交
110 111 112
            conflict_handler='resolve',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog=self.example_of_use)
113
        parser.add_argument(
114
            '-V', '--version', action='version', version=version)
N
Nicolas Hennion 已提交
115
        parser.add_argument('-d', '--debug', action='store_true', default=False,
A
Alessio Sergi 已提交
116
                            dest='debug', help='enable debug mode')
A
Alessio Sergi 已提交
117
        parser.add_argument('-C', '--config', dest='conf_file',
A
Alessio Sergi 已提交
118
                            help='path to the configuration file')
119
        # Disable plugin
N
nicolargo 已提交
120 121 122 123
        parser.add_argument('--modules-list', '--module-list',
                            action='store_true', default=False,
                            dest='modules_list',
                            help='display modules (plugins & exports) list and exit')
124 125
        parser.add_argument('--disable-plugin', dest='disable_plugin',
                            help='disable plugin (comma separed list)')
N
nicolargo 已提交
126 127
        parser.add_argument('--disable-process', action='store_true', default=False,
                            dest='disable_process', help='disable process module')
128
        # Enable or disable option
129 130
        parser.add_argument('--disable-webui', action='store_true', default=False,
                            dest='disable_webui', help='disable the Web Interface')
131 132 133
        parser.add_argument('--light', '--enable-light', action='store_true',
                            default=False, dest='enable_light',
                            help='light mode for Curses UI (disable all but top menu)')
N
nicolargo 已提交
134 135 136 137
        parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
                            dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs')
        parser.add_argument('-1', '--percpu', action='store_true', default=False,
                            dest='percpu', help='start Glances in per CPU mode')
138
        parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
A
Alessio Sergi 已提交
139
                            default=False, dest='disable_left_sidebar',
140
                            help='disable network, disk I/O, FS and sensors modules')
N
nicolargo 已提交
141 142 143 144 145 146 147
        parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
                            dest='disable_quicklook', help='disable quick look module')
        parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
                            dest='full_quicklook', help='disable all but quick look and load')
        parser.add_argument('-5', '--disable-top', action='store_true',
                            default=False, dest='disable_top',
                            help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
148 149
        parser.add_argument('-6', '--meangpu', action='store_true', default=False,
                            dest='meangpu', help='start Glances in mean GPU mode')
N
Nicolargo 已提交
150 151
        parser.add_argument('--disable-history', action='store_true', default=False,
                            dest='disable_history', help='disable stats history')
152
        parser.add_argument('--disable-bold', action='store_true', default=False,
A
Alessio Sergi 已提交
153
                            dest='disable_bold', help='disable bold mode in the terminal')
154
        parser.add_argument('--disable-bg', action='store_true', default=False,
155
                            dest='disable_bg', help='disable background colors in the terminal')
156 157
        parser.add_argument('--enable-irq', action='store_true', default=False,
                            dest='enable_irq', help='enable IRQ module'),
N
Nicolargo 已提交
158
        parser.add_argument('--enable-process-extended', action='store_true', default=False,
A
Alessio Sergi 已提交
159
                            dest='enable_process_extended', help='enable extended stats on top process')
160
        # Export modules feature
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
        parser.add_argument('--export', dest='export',
                            help='enable export module (comma separed list)')
        # To be removed on https://github.com/nicolargo/glances/issues/1206
        # parser.add_argument('--export-graph', action='store_true', default=None,
        #                     dest='export_graph', help='export stats to graphs')
        # parser.add_argument('--path-graph', default=tempfile.gettempdir(),
        #                     dest='path_graph', help='set the export path for graphs (default is {})'.format(tempfile.gettempdir()))
        parser.add_argument('--export-csv-file',
                            default='./glances.csv',
                            dest='export_csv_file',
                            help='file path for CSV exporter')
        parser.add_argument('--export-json-file',
                            default='./glances.json',
                            dest='export_json_file',
                            help='file path for JSON exporter')
N
Nicolargo 已提交
176 177
        # Client/Server option
        parser.add_argument('-c', '--client', dest='client',
A
Alessio Sergi 已提交
178
                            help='connect to a Glances server by IPv4/IPv6 address or hostname')
N
Nicolargo 已提交
179
        parser.add_argument('-s', '--server', action='store_true', default=False,
A
Alessio Sergi 已提交
180
                            dest='server', help='run Glances in server mode')
181
        parser.add_argument('--browser', action='store_true', default=False,
A
Alessio Sergi 已提交
182
                            dest='browser', help='start the client browser (list of servers)')
183
        parser.add_argument('--disable-autodiscover', action='store_true', default=False,
A
Alessio Sergi 已提交
184
                            dest='disable_autodiscover', help='disable autodiscover feature')
185
        parser.add_argument('-p', '--port', default=None, type=int, dest='port',
186
                            help='define the client/server TCP port [default: {}]'.format(self.server_port))
N
Nicolargo 已提交
187
        parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
A
Alessio Sergi 已提交
188
                            help='bind server to the given IPv4/IPv6 address or hostname')
189 190
        parser.add_argument('--username', action='store_true', default=False, dest='username_prompt',
                            help='define a client/server username')
A
Alessio Sergi 已提交
191
        parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
A
Alessio Sergi 已提交
192
                            help='define a client/server password')
193
        parser.add_argument('--snmp-community', default='public', dest='snmp_community',
A
Alessio Sergi 已提交
194
                            help='SNMP community')
A
Alessio Sergi 已提交
195
        parser.add_argument('--snmp-port', default=161, type=int,
A
Alessio Sergi 已提交
196
                            dest='snmp_port', help='SNMP port')
N
Nicolargo 已提交
197
        parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
A
Alessio Sergi 已提交
198
                            help='SNMP version (1, 2c or 3)')
N
Nicolargo 已提交
199
        parser.add_argument('--snmp-user', default='private', dest='snmp_user',
A
Alessio Sergi 已提交
200
                            help='SNMP username (only for SNMPv3)')
N
Nicolargo 已提交
201
        parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
A
Alessio Sergi 已提交
202
                            help='SNMP authentication key (only for SNMPv3)')
203
        parser.add_argument('--snmp-force', action='store_true', default=False,
A
Alessio Sergi 已提交
204
                            dest='snmp_force', help='force SNMP mode')
205
        parser.add_argument('-t', '--time', default=self.refresh_time, type=float,
206
                            dest='time', help='set refresh time in seconds [default: {} sec]'.format(self.refresh_time))
A
Alessio Sergi 已提交
207
        parser.add_argument('-w', '--webserver', action='store_true', default=False,
A
Alessio Sergi 已提交
208
                            dest='webserver', help='run Glances in web server mode (bottle needed)')
209 210
        parser.add_argument('--cached-time', default=self.cached_time, type=int,
                            dest='cached_time', help='set the server cache time [default: {} sec]'.format(self.cached_time))
N
nicolargo 已提交
211 212
        parser.add_argument('--open-web-browser', action='store_true', default=False,
                            dest='open_web_browser', help='try to open the Web UI in the default Web browser')
213
        # Display options
N
nicolargo 已提交
214 215
        parser.add_argument('-q', '--quiet', default=False, action='store_true',
                            dest='quiet', help='do not display the curses interface')
N
Nicolargo 已提交
216
        parser.add_argument('-f', '--process-filter', default=None, type=str,
A
Alessio Sergi 已提交
217
                            dest='process_filter', help='set the process filter pattern (regular expression)')
218
        parser.add_argument('--process-short-name', action='store_true', default=False,
A
Alessio Sergi 已提交
219
                            dest='process_short_name', help='force short name for processes name')
A
Alessio Sergi 已提交
220
        if not WINDOWS:
221
            parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
222
                                dest='no_kernel_threads', help='hide kernel threads in process list (not available on Windows)')
A
Alessio Sergi 已提交
223
        if LINUX:
224
            parser.add_argument('--tree', action='store_true', default=False,
225
                                dest='process_tree', help='display processes as a tree (Linux only)')
N
Nicolargo 已提交
226
        parser.add_argument('-b', '--byte', action='store_true', default=False,
A
Alessio Sergi 已提交
227
                            dest='byte', help='display network rate in byte per second')
228 229
        parser.add_argument('--diskio-show-ramfs', action='store_true', default=False,
                            dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin')
230 231
        parser.add_argument('--diskio-iops', action='store_true', default=False,
                            dest='diskio_iops', help='show IO per second in the DiskIO plugin')
N
nicolargo 已提交
232 233
        parser.add_argument('--fahrenheit', action='store_true', default=False,
                            dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
A
Alessio Sergi 已提交
234
        parser.add_argument('--fs-free-space', action='store_true', default=False,
A
Alessio Sergi 已提交
235
                            dest='fs_free_space', help='display FS free space instead of used')
N
Nicolargo 已提交
236
        parser.add_argument('--theme-white', action='store_true', default=False,
A
Alessio Sergi 已提交
237
                            dest='theme_white', help='optimize display colors for white background')
238 239 240
        # Globals options
        parser.add_argument('--disable-check-update', action='store_true', default=False,
                            dest='disable_check_update', help='disable online Glances version ckeck')
A
Alessio Sergi 已提交
241 242 243 244 245 246
        return parser

    def parse_args(self):
        """Parse command line arguments."""
        args = self.init_args().parse_args()

247
        # Load the configuration file, if it exists
A
Alessio Sergi 已提交
248 249
        self.config = Config(args.conf_file)

N
Nicolas Hennion 已提交
250 251 252 253 254
        # Debug mode
        if args.debug:
            from logging import DEBUG
            logger.setLevel(DEBUG)

255 256 257 258 259
        # Plugins disable/enable
        if args.disable_plugin is not None:
            for p in args.disable_plugin.split(','):
                disable(args, p)

260 261 262 263 264
        # Exporters activation
        if args.export is not None:
            for p in args.export.split(','):
                setattr(args, 'export_' + p, True)

265
        # Client/server Port
266 267 268 269
        if args.port is None:
            if args.webserver:
                args.port = self.web_server_port
            else:
270
                args.port = self.server_port
N
nicolargo 已提交
271
        # Port in the -c URI #996
272 273
        if args.client is not None:
            args.client, args.port = (x if x else y for (x, y) in zip(args.client.partition(':')[::2], (args.client, args.port)))
274

275 276 277 278
        # Autodiscover
        if args.disable_autodiscover:
            logger.info("Auto discover mode is disabled")

279 280 281 282
        # By default Windows is started in Web mode
        if WINDOWS:
            args.webserver = True

283
        # In web server mode, defaul refresh time: 5 sec
A
Alessio Sergi 已提交
284
        if args.webserver:
285
            args.time = 5
286
            args.process_short_name = True
N
Nicolas Hennion 已提交
287 288

        # Server or client login/password
289 290 291 292 293
        if args.username_prompt:
            # Every username needs a password
            args.password_prompt = True
            # Prompt username
            if args.server:
N
nicolargo 已提交
294 295
                args.username = self.__get_username(
                    description='Define the Glances server username: ')
296
            elif args.webserver:
N
nicolargo 已提交
297 298
                args.username = self.__get_username(
                    description='Define the Glances webserver username: ')
299
            elif args.client:
N
nicolargo 已提交
300 301
                args.username = self.__get_username(
                    description='Enter the Glances server username: ')
302 303
        else:
            # Default user name is 'glances'
304
            args.username = self.username
305

306
        if args.password_prompt:
N
Nicolargo 已提交
307
            # Interactive or file password
A
Alessio Sergi 已提交
308
            if args.server:
N
Nicolas Hennion 已提交
309
                args.password = self.__get_password(
N
nicolargo 已提交
310 311
                    description='Define the Glances server password ({} username): '.format(
                        args.username),
312 313
                    confirm=True,
                    username=args.username)
314 315
            elif args.webserver:
                args.password = self.__get_password(
N
nicolargo 已提交
316 317
                    description='Define the Glances webserver password ({} username): '.format(
                        args.username),
318 319
                    confirm=True,
                    username=args.username)
A
Alessio Sergi 已提交
320
            elif args.client:
N
Nicolas Hennion 已提交
321
                args.password = self.__get_password(
N
nicolargo 已提交
322 323
                    description='Enter the Glances server password ({} username): '.format(
                        args.username),
324 325
                    clear=True,
                    username=args.username)
N
Nicolas Hennion 已提交
326 327 328 329
        else:
            # Default is no password
            args.password = self.password

A
Alessio Sergi 已提交
330 331 332 333 334 335 336
        # By default help is hidden
        args.help_tag = False

        # Display Rx and Tx, not the sum for the network
        args.network_sum = False
        args.network_cumul = False

337 338 339 340
        # Manage light mode
        if args.enable_light:
            logger.info("Light mode is on")
            args.disable_left_sidebar = True
341 342 343 344
            disable(args, 'process')
            disable(args, 'alert')
            disable(args, 'amps')
            disable(args, 'docker')
345

346 347
        # Manage full quicklook option
        if args.full_quicklook:
348 349 350 351 352 353
            logger.info("Full quicklook mode")
            enable(args, 'quicklook')
            disable(args, 'cpu')
            disable(args, 'mem')
            disable(args, 'memswap')
            enable(args, 'load')
354 355 356 357

        # Manage disable_top option
        if args.disable_top:
            logger.info("Disable top menu")
358 359 360 361 362
            disable(args, 'quicklook')
            disable(args, 'cpu')
            disable(args, 'mem')
            disable(args, 'memswap')
            disable(args, 'load')
363

364 365 366
        # Control parameter and exit if it is not OK
        self.args = args

367
        # Export is only available in standalone or client mode (issue #614)
368
        export_tag = self.args.export is not None and any(self.args.export)
369 370 371
        if WINDOWS and export_tag:
            # On Windows, export is possible but only in quiet mode
            # See issue #1038
372
            logger.info("On Windows OS, export disable the Web interface")
373 374
            self.args.quiet = True
            self.args.webserver = False
375 376
        elif not (self.is_standalone() or self.is_client()) and export_tag:
            logger.critical("Export is only available in standalone or client mode")
377 378
            sys.exit(2)

379
        # Filter is only available in standalone mode
380
        if args.process_filter is not None and not self.is_standalone():
N
nicolargo 已提交
381 382
            logger.critical(
                "Process filter is only available in standalone mode")
383 384
            sys.exit(2)

385
        # Check graph output path
386 387 388 389 390 391 392
        # To be removed on https://github.com/nicolargo/glances/issues/1206
        # if args.export_graph and args.path_graph is not None:
        #     if not os.access(args.path_graph, os.W_OK):
        #         logger.critical("Graphs output path {} doesn't exist or is not writable".format(args.path_graph))
        #         sys.exit(2)
        #     logger.debug(
        #         "Graphs output path is set to {}".format(args.path_graph))
N
Nicolargo 已提交
393
        # For export graph, history is mandatory
394 395 396
        # if args.export_graph and args.disable_history:
        #     logger.critical("Can not export graph if history is disabled")
        #     sys.exit(2)
N
Nicolargo 已提交
397

398
        # Disable HDDTemp if sensors are disabled
399 400
        if getattr(args, 'disable_sensors', False):
            disable(args, 'hddtemp')
401 402
            logger.debug("Sensors and HDDTemp are disabled")

N
Nicolas Hennion 已提交
403
        return args
N
Nicolas Hennion 已提交
404

N
Nicolas Hennion 已提交
405
    def is_standalone(self):
A
PEP 257  
Alessio Sergi 已提交
406
        """Return True if Glances is running in standalone mode."""
407 408 409 410
        return (not self.args.client and
                not self.args.browser and
                not self.args.server and
                not self.args.webserver)
N
Nicolas Hennion 已提交
411 412

    def is_client(self):
A
PEP 257  
Alessio Sergi 已提交
413
        """Return True if Glances is running in client mode."""
414
        return (self.args.client or self.args.browser) and not self.args.server
415

416 417
    def is_client_browser(self):
        """Return True if Glances is running in client browser mode."""
418
        return self.args.browser and not self.args.server
N
Nicolas Hennion 已提交
419 420

    def is_server(self):
A
PEP 257  
Alessio Sergi 已提交
421
        """Return True if Glances is running in server mode."""
422
        return not self.args.client and self.args.server
N
Nicolas Hennion 已提交
423

424
    def is_webserver(self):
A
PEP 257  
Alessio Sergi 已提交
425
        """Return True if Glances is running in Web server mode."""
426
        return not self.args.client and self.args.webserver
N
Nicolas Hennion 已提交
427

428
    def get_config(self):
A
PEP 257  
Alessio Sergi 已提交
429
        """Return configuration file object."""
430
        return self.config
N
Nicolas Hennion 已提交
431 432

    def get_args(self):
A
PEP 257  
Alessio Sergi 已提交
433
        """Return the arguments."""
A
Alessio Sergi 已提交
434
        return self.args
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

    def get_mode(self):
        """Return the mode."""
        return self.mode

    def __get_username(self, description=''):
        """Read an username from the command line.
        """
        return input(description)

    def __get_password(self, description='', confirm=False, clear=False, username='glances'):
        """Read a password from the command line.

        - if confirm = True, with confirmation
        - if clear = True, plain (clear password)
        """
        from glances.password import GlancesPassword
        password = GlancesPassword(username=username)
        return password.get_password(description, confirm, clear)