Completed
Push — master ( a1acfe...a11662 )
by Nicolas
01:53 queued 19s
created

GlancesMain   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 435
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
c 8
b 0
f 0
dl 0
loc 435
rs 8.439
wmc 47

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 4 1
B init_args() 0 178 3
A is_client() 0 3 1
A __get_username() 0 4 1
A is_webserver() 0 3 1
F parse_args() 0 154 33
A is_standalone() 0 6 1
A __get_password() 0 9 1
A get_mode() 0 3 1
A is_server() 0 3 1
A get_config() 0 3 1
A is_client_browser() 0 3 1
A get_args() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like GlancesMain often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2017 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Glances main class."""
21
22
import argparse
23
import os
24
import sys
25
import tempfile
26
27
from glances import __version__, psutil_version
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
32
33
34
class GlancesMain(object):
35
36
    """Main class to manage Glances instance."""
37
38
    # Default stats' refresh time is 3 seconds
39
    refresh_time = 3
40
    # Set the default cache lifetime to 1 second (only for server)
41
    cached_time = 1
42
    # By default, Glances is ran in standalone mode (no client/server)
43
    client_tag = False
44
    # Server TCP port number (default is 61209)
45
    server_port = 61209
46
    # Web Server TCP port number (default is 61208)
47
    web_server_port = 61208
48
    # Default username/password for client/server mode
49
    username = "glances"
50
    password = ""
51
52
    # Examples of use
53
    example_of_use = """
54
Examples of use:
55
  Monitor local machine (standalone mode):
56
    $ glances
57
58
  Monitor local machine with the Web interface (Web UI):
59
    $ glances -w
60
    Glances web server started on http://0.0.0.0:61208/
61
62
  Monitor local machine and export stats to a CSV file (standalone mode):
63
    $ glances --export-csv /tmp/glances.csv
64
65
  Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode):
66
    $ glances -t 5 --export-influxdb
67
68
  Start a Glances server (server mode):
69
    $ glances -s
70
71
  Connect Glances to a Glances server (client mode):
72
    $ glances -c <ip_server>
73
74
  Connect Glances to a Glances server and export stats to a StatsD server (client mode):
75
    $ glances -c <ip_server> --export-statsd
76
77
  Start the client browser (browser mode):
78
    $ glances --browser
79
"""
80
81
    def __init__(self):
82
        """Manage the command line arguments."""
83
        # Read the command line arguments
84
        self.args = self.parse_args()
85
86
    def init_args(self):
87
        """Init all the command line arguments."""
88
        version = "Glances v" + __version__ + " with psutil v" + psutil_version
89
        parser = argparse.ArgumentParser(
90
            prog='glances',
91
            conflict_handler='resolve',
92
            formatter_class=argparse.RawDescriptionHelpFormatter,
93
            epilog=self.example_of_use)
94
        parser.add_argument(
95
            '-V', '--version', action='version', version=version)
96
        parser.add_argument('-d', '--debug', action='store_true', default=False,
97
                            dest='debug', help='enable debug mode')
98
        parser.add_argument('-C', '--config', dest='conf_file',
99
                            help='path to the configuration file')
100
        # Enable or disable option on startup
101
        parser.add_argument('--disable-alert', action='store_true', default=False,
102
                            dest='disable_alert', help='disable alert module')
103
        parser.add_argument('--disable-amps', action='store_true', default=False,
104
                            dest='disable_amps', help='disable applications monitoring process (AMP) module')
105
        parser.add_argument('--disable-cloud', action='store_true', default=False,
106
                            dest='disable_cloud', help='disable Cloud module')
107
        parser.add_argument('--disable-cpu', action='store_true', default=False,
108
                            dest='disable_cpu', help='disable CPU module')
109
        parser.add_argument('--disable-diskio', action='store_true', default=False,
110
                            dest='disable_diskio', help='disable disk I/O module')
111
        parser.add_argument('--disable-docker', action='store_true', default=False,
112
                            dest='disable_docker', help='disable Docker module')
113
        parser.add_argument('--disable-folders', action='store_true', default=False,
114
                            dest='disable_folders', help='disable folder module')
115
        parser.add_argument('--disable-fs', action='store_true', default=False,
116
                            dest='disable_fs', help='disable filesystem module')
117
        parser.add_argument('--disable-gpu', action='store_true', default=False,
118
                            dest='disable_gpu', help='disable GPU module')
119
        parser.add_argument('--disable-hddtemp', action='store_true', default=False,
120
                            dest='disable_hddtemp', help='disable HD temperature module')
121
        parser.add_argument('--disable-ip', action='store_true', default=False,
122
                            dest='disable_ip', help='disable IP module')
123
        parser.add_argument('--disable-load', action='store_true', default=False,
124
                            dest='disable_load', help='disable load module')
125
        parser.add_argument('--disable-mem', action='store_true', default=False,
126
                            dest='disable_mem', help='disable memory module')
127
        parser.add_argument('--disable-memswap', action='store_true', default=False,
128
                            dest='disable_memswap', help='disable memory swap module')
129
        parser.add_argument('--disable-network', action='store_true', default=False,
130
                            dest='disable_network', help='disable network module')
131
        parser.add_argument('--disable-now', action='store_true', default=False,
132
                            dest='disable_now', help='disable current time module')
133
        parser.add_argument('--disable-ports', action='store_true', default=False,
134
                            dest='disable_ports', help='disable ports scanner module')
135
        parser.add_argument('--disable-process', action='store_true', default=False,
136
                            dest='disable_process', help='disable process module')
137
        parser.add_argument('--disable-raid', action='store_true', default=False,
138
                            dest='disable_raid', help='disable RAID module')
139
        parser.add_argument('--disable-sensors', action='store_true', default=False,
140
                            dest='disable_sensors', help='disable sensors module')
141
        parser.add_argument('--disable-wifi', action='store_true', default=False,
142
                            dest='disable_wifi', help='disable wifi module')
143
        parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
144
                            dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs')
145
        parser.add_argument('-1', '--percpu', action='store_true', default=False,
146
                            dest='percpu', help='start Glances in per CPU mode')
147
        parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
148
                            default=False, dest='disable_left_sidebar',
149
                            help='disable network, disk I/O, FS and sensors modules')
150
        parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
151
                            dest='disable_quicklook', help='disable quick look module')
152
        parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
153
                            dest='full_quicklook', help='disable all but quick look and load')
154
        parser.add_argument('-5', '--disable-top', action='store_true',
155
                            default=False, dest='disable_top',
156
                            help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
157
        parser.add_argument('-6', '--meangpu', action='store_true', default=False,
158
                            dest='meangpu', help='start Glances in mean GPU mode')
159
        parser.add_argument('--disable-history', action='store_true', default=False,
160
                            dest='disable_history', help='disable stats history')
161
        parser.add_argument('--disable-bold', action='store_true', default=False,
162
                            dest='disable_bold', help='disable bold mode in the terminal')
163
        parser.add_argument('--disable-bg', action='store_true', default=False,
164
                            dest='disable_bg', help='disable background colors in the terminal')
165
        parser.add_argument('--enable-irq', action='store_true', default=False,
166
                            dest='enable_irq', help='enable IRQ module'),
167
        parser.add_argument('--enable-process-extended', action='store_true', default=False,
168
                            dest='enable_process_extended', help='enable extended stats on top process')
169
        # Export modules feature
170
        parser.add_argument('--export-graph', action='store_true', default=None,
171
                            dest='export_graph', help='export stats to graphs')
172
        parser.add_argument('--path-graph', default=tempfile.gettempdir(),
173
                            dest='path_graph', help='set the export path for graphs (default is {})'.format(tempfile.gettempdir()))
174
        parser.add_argument('--export-csv', default=None,
175
                            dest='export_csv', help='export stats to a CSV file')
176
        parser.add_argument('--export-cassandra', action='store_true', default=False,
177
                            dest='export_cassandra', help='export stats to a Cassandra or Scylla server (cassandra lib needed)')
178
        parser.add_argument('--export-couchdb', action='store_true', default=False,
179
                            dest='export_couchdb', help='export stats to a CouchDB server (couch lib needed)')
180
        parser.add_argument('--export-elasticsearch', action='store_true', default=False,
181
                            dest='export_elasticsearch', help='export stats to an ElasticSearch server (elasticsearch lib needed)')
182
        parser.add_argument('--export-influxdb', action='store_true', default=False,
183
                            dest='export_influxdb', help='export stats to an InfluxDB server (influxdb lib needed)')
184
        parser.add_argument('--export-kafka', action='store_true', default=False,
185
                            dest='export_kafka', help='export stats to a Kafka server (kafka-python lib needed)')
186
        parser.add_argument('--export-opentsdb', action='store_true', default=False,
187
                            dest='export_opentsdb', help='export stats to an OpenTSDB server (potsdb lib needed)')
188
        parser.add_argument('--export-prometheus', action='store_true', default=False,
189
                            dest='export_prometheus', help='export stats to a Prometheus exporter (prometheus_client lib needed)')
190
        parser.add_argument('--export-rabbitmq', action='store_true', default=False,
191
                            dest='export_rabbitmq', help='export stats to rabbitmq broker (pika lib needed)')
192
        parser.add_argument('--export-riemann', action='store_true', default=False,
193
                            dest='export_riemann', help='export stats to riemann broker (bernhard lib needed)')
194
        parser.add_argument('--export-statsd', action='store_true', default=False,
195
                            dest='export_statsd', help='export stats to a StatsD server (statsd lib needed)')
196
        parser.add_argument('--export-zeromq', action='store_true', default=False,
197
                            dest='export_zeromq', help='export stats to a ZeroMQ server (pyzmq lib needed)')
198
        # Client/Server option
199
        parser.add_argument('-c', '--client', dest='client',
200
                            help='connect to a Glances server by IPv4/IPv6 address or hostname')
201
        parser.add_argument('-s', '--server', action='store_true', default=False,
202
                            dest='server', help='run Glances in server mode')
203
        parser.add_argument('--browser', action='store_true', default=False,
204
                            dest='browser', help='start the client browser (list of servers)')
205
        parser.add_argument('--disable-autodiscover', action='store_true', default=False,
206
                            dest='disable_autodiscover', help='disable autodiscover feature')
207
        parser.add_argument('-p', '--port', default=None, type=int, dest='port',
208
                            help='define the client/server TCP port [default: {}]'.format(self.server_port))
209
        parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
210
                            help='bind server to the given IPv4/IPv6 address or hostname')
211
        parser.add_argument('--username', action='store_true', default=False, dest='username_prompt',
212
                            help='define a client/server username')
213
        parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
214
                            help='define a client/server password')
215
        parser.add_argument('--snmp-community', default='public', dest='snmp_community',
216
                            help='SNMP community')
217
        parser.add_argument('--snmp-port', default=161, type=int,
218
                            dest='snmp_port', help='SNMP port')
219
        parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
220
                            help='SNMP version (1, 2c or 3)')
221
        parser.add_argument('--snmp-user', default='private', dest='snmp_user',
222
                            help='SNMP username (only for SNMPv3)')
223
        parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
224
                            help='SNMP authentication key (only for SNMPv3)')
225
        parser.add_argument('--snmp-force', action='store_true', default=False,
226
                            dest='snmp_force', help='force SNMP mode')
227
        parser.add_argument('-t', '--time', default=self.refresh_time, type=float,
228
                            dest='time', help='set refresh time in seconds [default: {} sec]'.format(self.refresh_time))
229
        parser.add_argument('-w', '--webserver', action='store_true', default=False,
230
                            dest='webserver', help='run Glances in web server mode (bottle needed)')
231
        parser.add_argument('--cached-time', default=self.cached_time, type=int,
232
                            dest='cached_time', help='set the server cache time [default: {} sec]'.format(self.cached_time))
233
        parser.add_argument('--open-web-browser', action='store_true', default=False,
234
                            dest='open_web_browser', help='try to open the Web UI in the default Web browser')
235
        # Display options
236
        parser.add_argument('-q', '--quiet', default=False, action='store_true',
237
                            dest='quiet', help='do not display the curses interface')
238
        parser.add_argument('-f', '--process-filter', default=None, type=str,
239
                            dest='process_filter', help='set the process filter pattern (regular expression)')
240
        parser.add_argument('--process-short-name', action='store_true', default=False,
241
                            dest='process_short_name', help='force short name for processes name')
242
        if not WINDOWS:
243
            parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
244
                                dest='no_kernel_threads', help='hide kernel threads in process list')
245
        if LINUX:
246
            parser.add_argument('--tree', action='store_true', default=False,
247
                                dest='process_tree', help='display processes as a tree')
248
        parser.add_argument('-b', '--byte', action='store_true', default=False,
249
                            dest='byte', help='display network rate in byte per second')
250
        parser.add_argument('--diskio-show-ramfs', action='store_true', default=False,
251
                            dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin')
252
        parser.add_argument('--diskio-iops', action='store_true', default=False,
253
                            dest='diskio_iops', help='show IO per second in the DiskIO plugin')
254
        parser.add_argument('--fahrenheit', action='store_true', default=False,
255
                            dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
256
        parser.add_argument('--fs-free-space', action='store_true', default=False,
257
                            dest='fs_free_space', help='display FS free space instead of used')
258
        parser.add_argument('--theme-white', action='store_true', default=False,
259
                            dest='theme_white', help='optimize display colors for white background')
260
        # Globals options
261
        parser.add_argument('--disable-check-update', action='store_true', default=False,
262
                            dest='disable_check_update', help='disable online Glances version ckeck')
263
        return parser
264
265
    def parse_args(self):
266
        """Parse command line arguments."""
267
        args = self.init_args().parse_args()
268
269
        # Load the configuration file, if it exists
270
        self.config = Config(args.conf_file)
271
272
        # Debug mode
273
        if args.debug:
274
            from logging import DEBUG
275
            logger.setLevel(DEBUG)
276
277
        # Client/server Port
278
        if args.port is None:
279
            if args.webserver:
280
                args.port = self.web_server_port
281
            else:
282
                args.port = self.server_port
283
        # Port in the -c URI #996
284
        if args.client is not None:
285
            args.client, args.port = (x if x else y for (x, y) in zip(args.client.partition(':')[::2], (args.client, args.port)))
286
287
        # Autodiscover
288
        if args.disable_autodiscover:
289
            logger.info("Auto discover mode is disabled")
290
291
        # By default Windows is started in Web mode
292
        if WINDOWS:
293
            args.webserver = True
294
295
        # In web server mode
296
        if args.webserver:
297
            args.process_short_name = True
298
299
        # Server or client login/password
300
        if args.username_prompt:
301
            # Every username needs a password
302
            args.password_prompt = True
303
            # Prompt username
304
            if args.server:
305
                args.username = self.__get_username(
306
                    description='Define the Glances server username: ')
307
            elif args.webserver:
308
                args.username = self.__get_username(
309
                    description='Define the Glances webserver username: ')
310
            elif args.client:
311
                args.username = self.__get_username(
312
                    description='Enter the Glances server username: ')
313
        else:
314
            # Default user name is 'glances'
315
            args.username = self.username
316
317
        if args.password_prompt:
318
            # Interactive or file password
319
            if args.server:
320
                args.password = self.__get_password(
321
                    description='Define the Glances server password ({} username): '.format(
322
                        args.username),
323
                    confirm=True,
324
                    username=args.username)
325
            elif args.webserver:
326
                args.password = self.__get_password(
327
                    description='Define the Glances webserver password ({} username): '.format(
328
                        args.username),
329
                    confirm=True,
330
                    username=args.username)
331
            elif args.client:
332
                args.password = self.__get_password(
333
                    description='Enter the Glances server password ({} username): '.format(
334
                        args.username),
335
                    clear=True,
336
                    username=args.username)
337
        else:
338
            # Default is no password
339
            args.password = self.password
340
341
        # By default help is hidden
342
        args.help_tag = False
343
344
        # Display Rx and Tx, not the sum for the network
345
        args.network_sum = False
346
        args.network_cumul = False
347
348
        # Manage full quicklook option
349
        if args.full_quicklook:
350
            logger.info("Disable QuickLook menu")
351
            args.disable_quicklook = False
352
            args.disable_cpu = True
353
            args.disable_mem = True
354
            args.disable_memswap = True
355
            args.disable_load = False
356
357
        # Manage disable_top option
358
        if args.disable_top:
359
            logger.info("Disable top menu")
360
            args.disable_quicklook = True
361
            args.disable_cpu = True
362
            args.disable_mem = True
363
            args.disable_memswap = True
364
            args.disable_load = True
365
366
        # Control parameter and exit if it is not OK
367
        self.args = args
368
369
        # Export is only available in standalone or client mode (issue #614)
370
        export_tag = (
371
            args.export_csv or
372
            args.export_cassandra or
373
            args.export_couchdb or
374
            args.export_elasticsearch or
375
            args.export_influxdb or
376
            args.export_kafka or
377
            args.export_opentsdb or
378
            args.export_prometheus or
379
            args.export_rabbitmq or
380
            args.export_riemann or
381
            args.export_statsd or
382
            args.export_zeromq
383
        )
384
        if WINDOWS and export_tag:
385
            # On Windows, export is possible but only in quiet mode
386
            # See issue #1038
387
            logger.info("On Windows OS, export disable the Web interface")
388
            self.args.quiet = True
389
            self.args.webserver = False
390
        elif not (self.is_standalone() or self.is_client()) and export_tag:
391
            logger.critical("Export is only available in standalone or client mode")
392
            sys.exit(2)
393
394
        # Filter is only available in standalone mode
395
        if args.process_filter is not None and not self.is_standalone():
396
            logger.critical(
397
                "Process filter is only available in standalone mode")
398
            sys.exit(2)
399
400
        # Check graph output path
401
        if args.export_graph and args.path_graph is not None:
402
            if not os.access(args.path_graph, os.W_OK):
403
                logger.critical("Graphs output path {} doesn't exist or is not writable".format(args.path_graph))
404
                sys.exit(2)
405
            logger.debug(
406
                "Graphs output path is set to {}".format(args.path_graph))
407
408
        # For export graph, history is mandatory
409
        if args.export_graph and args.disable_history:
410
            logger.critical("Can not export graph if history is disabled")
411
            sys.exit(2)
412
413
        # Disable HDDTemp if sensors are disabled
414
        if args.disable_sensors:
415
            args.disable_hddtemp = True
416
            logger.debug("Sensors and HDDTemp are disabled")
417
418
        return args
419
420
    def is_standalone(self):
421
        """Return True if Glances is running in standalone mode."""
422
        return (not self.args.client and
423
                not self.args.browser and
424
                not self.args.server and
425
                not self.args.webserver)
426
427
    def is_client(self):
428
        """Return True if Glances is running in client mode."""
429
        return (self.args.client or self.args.browser) and not self.args.server
430
431
    def is_client_browser(self):
432
        """Return True if Glances is running in client browser mode."""
433
        return self.args.browser and not self.args.server
434
435
    def is_server(self):
436
        """Return True if Glances is running in server mode."""
437
        return not self.args.client and self.args.server
438
439
    def is_webserver(self):
440
        """Return True if Glances is running in Web server mode."""
441
        return not self.args.client and self.args.webserver
442
443
    def get_config(self):
444
        """Return configuration file object."""
445
        return self.config
446
447
    def get_args(self):
448
        """Return the arguments."""
449
        return self.args
450
451
    def get_mode(self):
452
        """Return the mode."""
453
        return self.mode
454
455
    def __get_username(self, description=''):
456
        """Read an username from the command line.
457
        """
458
        return input(description)
459
460
    def __get_password(self, description='', confirm=False, clear=False, username='glances'):
461
        """Read a password from the command line.
462
463
        - if confirm = True, with confirmation
464
        - if clear = True, plain (clear password)
465
        """
466
        from glances.password import GlancesPassword
467
        password = GlancesPassword(username=username)
468
        return password.get_password(description, confirm, clear)
469