main.py 22.6 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
  Monitor local machine with the Web interface and start Restful server:
68 69 70
    $ glances -w
    Glances web server started on http://0.0.0.0:61208/

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

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

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

81
  Start a Glances XML/RCP server (server mode):
82 83
    $ glances -s

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

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

  Start the client browser (browser mode):
    $ glances --browser
92 93 94

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

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

    def init_args(self):
        """Init all the command line arguments."""
104
        version = "Glances v" + __version__ + " with psutil v" + psutil_version
105
        parser = argparse.ArgumentParser(
A
Alessio Sergi 已提交
106
            prog='glances',
N
Nicolargo 已提交
107 108 109
            conflict_handler='resolve',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog=self.example_of_use)
110
        parser.add_argument(
111
            '-V', '--version', action='version', version=version)
N
Nicolas Hennion 已提交
112
        parser.add_argument('-d', '--debug', action='store_true', default=False,
A
Alessio Sergi 已提交
113
                            dest='debug', help='enable debug mode')
A
Alessio Sergi 已提交
114
        parser.add_argument('-C', '--config', dest='conf_file',
A
Alessio Sergi 已提交
115
                            help='path to the configuration file')
116 117 118
        # Disable plugin
        parser.add_argument('--disable-plugin', dest='disable_plugin',
                            help='disable plugin (comma separed list)')
N
nicolargo 已提交
119 120
        parser.add_argument('--disable-process', action='store_true', default=False,
                            dest='disable_process', help='disable process module')
121
        # Enable or disable option
122 123
        parser.add_argument('--disable-webui', action='store_true', default=False,
                            dest='disable_webui', help='disable the Web Interface')
124 125 126
        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 已提交
127 128 129 130
        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')
131
        parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
A
Alessio Sergi 已提交
132
                            default=False, dest='disable_left_sidebar',
133
                            help='disable network, disk I/O, FS and sensors modules')
N
nicolargo 已提交
134 135 136 137 138 139 140
        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)')
141 142
        parser.add_argument('-6', '--meangpu', action='store_true', default=False,
                            dest='meangpu', help='start Glances in mean GPU mode')
N
Nicolargo 已提交
143 144
        parser.add_argument('--disable-history', action='store_true', default=False,
                            dest='disable_history', help='disable stats history')
145
        parser.add_argument('--disable-bold', action='store_true', default=False,
A
Alessio Sergi 已提交
146
                            dest='disable_bold', help='disable bold mode in the terminal')
147
        parser.add_argument('--disable-bg', action='store_true', default=False,
148
                            dest='disable_bg', help='disable background colors in the terminal')
149 150
        parser.add_argument('--enable-irq', action='store_true', default=False,
                            dest='enable_irq', help='enable IRQ module'),
N
Nicolargo 已提交
151
        parser.add_argument('--enable-process-extended', action='store_true', default=False,
A
Alessio Sergi 已提交
152
                            dest='enable_process_extended', help='enable extended stats on top process')
153
        # Export modules feature
N
Nicolargo 已提交
154
        parser.add_argument('--export-graph', action='store_true', default=None,
N
nicolargo 已提交
155
                            dest='export_graph', help='export stats to graphs')
N
Nicolargo 已提交
156
        parser.add_argument('--path-graph', default=tempfile.gettempdir(),
157
                            dest='path_graph', help='set the export path for graphs (default is {})'.format(tempfile.gettempdir()))
158
        parser.add_argument('--export-csv', default=None,
A
Alessio Sergi 已提交
159
                            dest='export_csv', help='export stats to a CSV file')
J
Jude 已提交
160 161
        parser.add_argument('--export-json', default=None,
                            dest='export_json', help='export stats to a JSON file')
162 163
        parser.add_argument('--export-cassandra', action='store_true', default=False,
                            dest='export_cassandra', help='export stats to a Cassandra or Scylla server (cassandra lib needed)')
164 165
        parser.add_argument('--export-couchdb', action='store_true', default=False,
                            dest='export_couchdb', help='export stats to a CouchDB server (couch lib needed)')
166 167
        parser.add_argument('--export-elasticsearch', action='store_true', default=False,
                            dest='export_elasticsearch', help='export stats to an ElasticSearch server (elasticsearch lib needed)')
168 169
        parser.add_argument('--export-influxdb', action='store_true', default=False,
                            dest='export_influxdb', help='export stats to an InfluxDB server (influxdb lib needed)')
N
nicolargo 已提交
170 171
        parser.add_argument('--export-kafka', action='store_true', default=False,
                            dest='export_kafka', help='export stats to a Kafka server (kafka-python lib needed)')
172 173 174 175
        parser.add_argument('--export-opentsdb', action='store_true', default=False,
                            dest='export_opentsdb', help='export stats to an OpenTSDB server (potsdb lib needed)')
        parser.add_argument('--export-prometheus', action='store_true', default=False,
                            dest='export_prometheus', help='export stats to a Prometheus exporter (prometheus_client lib needed)')
A
Alessio Sergi 已提交
176
        parser.add_argument('--export-rabbitmq', action='store_true', default=False,
177
                            dest='export_rabbitmq', help='export stats to rabbitmq broker (pika lib needed)')
N
nicolargo 已提交
178 179
        parser.add_argument('--export-restful', action='store_true', default=False,
                            dest='export_restful', help='export stats to a Restful endpoint (requests lib needed)')
180 181
        parser.add_argument('--export-riemann', action='store_true', default=False,
                            dest='export_riemann', help='export stats to riemann broker (bernhard lib needed)')
182 183
        parser.add_argument('--export-statsd', action='store_true', default=False,
                            dest='export_statsd', help='export stats to a StatsD server (statsd lib needed)')
N
nicolargo 已提交
184 185
        parser.add_argument('--export-zeromq', action='store_true', default=False,
                            dest='export_zeromq', help='export stats to a ZeroMQ server (pyzmq lib needed)')
N
Nicolargo 已提交
186 187
        # Client/Server option
        parser.add_argument('-c', '--client', dest='client',
A
Alessio Sergi 已提交
188
                            help='connect to a Glances server by IPv4/IPv6 address or hostname')
N
Nicolargo 已提交
189
        parser.add_argument('-s', '--server', action='store_true', default=False,
A
Alessio Sergi 已提交
190
                            dest='server', help='run Glances in server mode')
191
        parser.add_argument('--browser', action='store_true', default=False,
A
Alessio Sergi 已提交
192
                            dest='browser', help='start the client browser (list of servers)')
193
        parser.add_argument('--disable-autodiscover', action='store_true', default=False,
A
Alessio Sergi 已提交
194
                            dest='disable_autodiscover', help='disable autodiscover feature')
195
        parser.add_argument('-p', '--port', default=None, type=int, dest='port',
196
                            help='define the client/server TCP port [default: {}]'.format(self.server_port))
N
Nicolargo 已提交
197
        parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
A
Alessio Sergi 已提交
198
                            help='bind server to the given IPv4/IPv6 address or hostname')
199 200
        parser.add_argument('--username', action='store_true', default=False, dest='username_prompt',
                            help='define a client/server username')
A
Alessio Sergi 已提交
201
        parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
A
Alessio Sergi 已提交
202
                            help='define a client/server password')
203
        parser.add_argument('--snmp-community', default='public', dest='snmp_community',
A
Alessio Sergi 已提交
204
                            help='SNMP community')
A
Alessio Sergi 已提交
205
        parser.add_argument('--snmp-port', default=161, type=int,
A
Alessio Sergi 已提交
206
                            dest='snmp_port', help='SNMP port')
N
Nicolargo 已提交
207
        parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
A
Alessio Sergi 已提交
208
                            help='SNMP version (1, 2c or 3)')
N
Nicolargo 已提交
209
        parser.add_argument('--snmp-user', default='private', dest='snmp_user',
A
Alessio Sergi 已提交
210
                            help='SNMP username (only for SNMPv3)')
N
Nicolargo 已提交
211
        parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
A
Alessio Sergi 已提交
212
                            help='SNMP authentication key (only for SNMPv3)')
213
        parser.add_argument('--snmp-force', action='store_true', default=False,
A
Alessio Sergi 已提交
214
                            dest='snmp_force', help='force SNMP mode')
215
        parser.add_argument('-t', '--time', default=self.refresh_time, type=float,
216
                            dest='time', help='set refresh time in seconds [default: {} sec]'.format(self.refresh_time))
A
Alessio Sergi 已提交
217
        parser.add_argument('-w', '--webserver', action='store_true', default=False,
A
Alessio Sergi 已提交
218
                            dest='webserver', help='run Glances in web server mode (bottle needed)')
219 220
        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 已提交
221 222
        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')
223
        # Display options
N
nicolargo 已提交
224 225
        parser.add_argument('-q', '--quiet', default=False, action='store_true',
                            dest='quiet', help='do not display the curses interface')
N
Nicolargo 已提交
226
        parser.add_argument('-f', '--process-filter', default=None, type=str,
A
Alessio Sergi 已提交
227
                            dest='process_filter', help='set the process filter pattern (regular expression)')
228
        parser.add_argument('--process-short-name', action='store_true', default=False,
A
Alessio Sergi 已提交
229
                            dest='process_short_name', help='force short name for processes name')
A
Alessio Sergi 已提交
230
        if not WINDOWS:
231
            parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
232
                                dest='no_kernel_threads', help='hide kernel threads in process list (not available on Windows)')
A
Alessio Sergi 已提交
233
        if LINUX:
234
            parser.add_argument('--tree', action='store_true', default=False,
235
                                dest='process_tree', help='display processes as a tree (Linux only)')
N
Nicolargo 已提交
236
        parser.add_argument('-b', '--byte', action='store_true', default=False,
A
Alessio Sergi 已提交
237
                            dest='byte', help='display network rate in byte per second')
238 239
        parser.add_argument('--diskio-show-ramfs', action='store_true', default=False,
                            dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin')
240 241
        parser.add_argument('--diskio-iops', action='store_true', default=False,
                            dest='diskio_iops', help='show IO per second in the DiskIO plugin')
N
nicolargo 已提交
242 243
        parser.add_argument('--fahrenheit', action='store_true', default=False,
                            dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
A
Alessio Sergi 已提交
244
        parser.add_argument('--fs-free-space', action='store_true', default=False,
A
Alessio Sergi 已提交
245
                            dest='fs_free_space', help='display FS free space instead of used')
N
Nicolargo 已提交
246
        parser.add_argument('--theme-white', action='store_true', default=False,
A
Alessio Sergi 已提交
247
                            dest='theme_white', help='optimize display colors for white background')
248 249 250
        # 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 已提交
251 252 253 254 255 256
        return parser

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

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

N
Nicolas Hennion 已提交
260 261 262 263 264
        # Debug mode
        if args.debug:
            from logging import DEBUG
            logger.setLevel(DEBUG)

265 266 267 268 269
        # Plugins disable/enable
        if args.disable_plugin is not None:
            for p in args.disable_plugin.split(','):
                disable(args, p)

270
        # Client/server Port
271 272 273 274
        if args.port is None:
            if args.webserver:
                args.port = self.web_server_port
            else:
275
                args.port = self.server_port
N
nicolargo 已提交
276
        # Port in the -c URI #996
277 278
        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)))
279

280 281 282 283
        # Autodiscover
        if args.disable_autodiscover:
            logger.info("Auto discover mode is disabled")

284 285 286 287
        # By default Windows is started in Web mode
        if WINDOWS:
            args.webserver = True

288
        # In web server mode, defaul refresh time: 5 sec
A
Alessio Sergi 已提交
289
        if args.webserver:
290
            args.time = 5
291
            args.process_short_name = True
N
Nicolas Hennion 已提交
292 293

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

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

A
Alessio Sergi 已提交
335 336 337 338 339 340 341
        # 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

342 343 344 345
        # Manage light mode
        if args.enable_light:
            logger.info("Light mode is on")
            args.disable_left_sidebar = True
346 347 348 349
            disable(args, 'process')
            disable(args, 'alert')
            disable(args, 'amps')
            disable(args, 'docker')
350

351 352
        # Manage full quicklook option
        if args.full_quicklook:
353 354 355 356 357 358
            logger.info("Full quicklook mode")
            enable(args, 'quicklook')
            disable(args, 'cpu')
            disable(args, 'mem')
            disable(args, 'memswap')
            enable(args, 'load')
359 360 361 362

        # Manage disable_top option
        if args.disable_top:
            logger.info("Disable top menu")
363 364 365 366 367
            disable(args, 'quicklook')
            disable(args, 'cpu')
            disable(args, 'mem')
            disable(args, 'memswap')
            disable(args, 'load')
368

369 370 371
        # Control parameter and exit if it is not OK
        self.args = args

372
        # Export is only available in standalone or client mode (issue #614)
373
        export_tag = any([getattr(args, a) for a in args.__dict__ if a.startswith('export_')])
374 375 376
        if WINDOWS and export_tag:
            # On Windows, export is possible but only in quiet mode
            # See issue #1038
377
            logger.info("On Windows OS, export disable the Web interface")
378 379
            self.args.quiet = True
            self.args.webserver = False
380 381
        elif not (self.is_standalone() or self.is_client()) and export_tag:
            logger.critical("Export is only available in standalone or client mode")
382 383
            sys.exit(2)

384
        # Filter is only available in standalone mode
385
        if args.process_filter is not None and not self.is_standalone():
N
nicolargo 已提交
386 387
            logger.critical(
                "Process filter is only available in standalone mode")
388 389
            sys.exit(2)

390
        # Check graph output path
N
nicolargo 已提交
391 392
        if args.export_graph and args.path_graph is not None:
            if not os.access(args.path_graph, os.W_OK):
393
                logger.critical("Graphs output path {} doesn't exist or is not writable".format(args.path_graph))
394
                sys.exit(2)
N
nicolargo 已提交
395
            logger.debug(
396
                "Graphs output path is set to {}".format(args.path_graph))
397

N
Nicolargo 已提交
398 399 400 401 402
        # For export graph, history is mandatory
        if args.export_graph and args.disable_history:
            logger.critical("Can not export graph if history is disabled")
            sys.exit(2)

403
        # Disable HDDTemp if sensors are disabled
404 405
        if getattr(args, 'disable_sensors', False):
            disable(args, 'hddtemp')
406 407
            logger.debug("Sensors and HDDTemp are disabled")

N
Nicolas Hennion 已提交
408
        return args
N
Nicolas Hennion 已提交
409

N
Nicolas Hennion 已提交
410
    def is_standalone(self):
A
PEP 257  
Alessio Sergi 已提交
411
        """Return True if Glances is running in standalone mode."""
412 413 414 415
        return (not self.args.client and
                not self.args.browser and
                not self.args.server and
                not self.args.webserver)
N
Nicolas Hennion 已提交
416 417

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

421 422
    def is_client_browser(self):
        """Return True if Glances is running in client browser mode."""
423
        return self.args.browser and not self.args.server
N
Nicolas Hennion 已提交
424 425

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

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

433
    def get_config(self):
A
PEP 257  
Alessio Sergi 已提交
434
        """Return configuration file object."""
435
        return self.config
N
Nicolas Hennion 已提交
436 437

    def get_args(self):
A
PEP 257  
Alessio Sergi 已提交
438
        """Return the arguments."""
A
Alessio Sergi 已提交
439
        return self.args
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

    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)