Completed
Push — master ( 1806d1...053f07 )
by Nicolas
01:42
created

GlancesMain.parse_args()   F

Complexity

Conditions 25

Size

Total Lines 115

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 25
c 1
b 0
f 0
dl 0
loc 115
rs 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like GlancesMain.parse_args() 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) 2015 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 __appname__, __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
41
    # Set the default cache lifetime to 1 second (only for server)
42
    # !!! Todo: configuration from the command line
43
    cached_time = 1
44
    # By default, Glances is ran in standalone mode (no client/server)
45
    client_tag = False
46
    # Server TCP port number (default is 61209)
47
    server_port = 61209
48
    # Web Server TCP port number (default is 61208)
49
    web_server_port = 61208
50
    # Default username/password for client/server mode
51
    username = "glances"
52
    password = ""
53
54
    # Exemple of use
55
    example_of_use = "\
56
Examples of use:\n\
57
\n\
58
Monitor local machine (standalone mode):\n\
59
  $ glances\n\
60
\n\
61
Monitor local machine with the Web interface (Web UI):\n\
62
  $ glances -w\n\
63
  Glances web server started on http://0.0.0.0:61208/\n\
64
\n\
65
Monitor local machine and export stats to a CSV file (standalone mode):\n\
66
  $ glances --export-csv\n\
67
\n\
68
Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode):\n\
69
  $ glances -t 5 --export-influxdb\n\
70
\n\
71
Start a Glances server (server mode):\n\
72
  $ glances -s\n\
73
\n\
74
Connect Glances to a Glances server (client mode):\n\
75
  $ glances -c <ip_server>\n\
76
\n\
77
Connect Glances to a Glances server and export stats to a StatsD server (client mode):\n\
78
  $ glances -c <ip_server> --export-statsd\n\
79
\n\
80
Start the client browser (browser mode):\n\
81
  $ glances --browser\n\
82
    "
83
84
    def __init__(self):
85
        """Manage the command line arguments."""
86
        self.args = self.parse_args()
87
88
    def init_args(self):
89
        """Init all the command line arguments."""
90
        version = "Glances v" + __version__ + " with psutil v" + psutil_version
91
        parser = argparse.ArgumentParser(
92
            prog=__appname__,
93
            conflict_handler='resolve',
94
            formatter_class=argparse.RawDescriptionHelpFormatter,
95
            epilog=self.example_of_use)
96
        parser.add_argument(
97
            '-V', '--version', action='version', version=version)
98
        parser.add_argument('-d', '--debug', action='store_true', default=False,
99
                            dest='debug', help='enable debug mode')
100
        parser.add_argument('-C', '--config', dest='conf_file',
101
                            help='path to the configuration file')
102
        # Enable or disable option on startup
103
        parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
104
                            dest='disable_quicklook', help='disable quick look module')
105
        parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
106
                            dest='full_quicklook', help='disable all but quick look and load')
107
        parser.add_argument('--disable-cpu', action='store_true', default=False,
108
                            dest='disable_cpu', help='disable CPU module')
109
        parser.add_argument('--disable-mem', action='store_true', default=False,
110
                            dest='disable_mem', help='disable memory module')
111
        parser.add_argument('--disable-swap', action='store_true', default=False,
112
                            dest='disable_swap', help='disable swap module')
113
        parser.add_argument('--disable-load', action='store_true', default=False,
114
                            dest='disable_load', help='disable load module')
115
        parser.add_argument('--disable-network', action='store_true', default=False,
116
                            dest='disable_network', help='disable network module')
117
        parser.add_argument('--disable-ip', action='store_true', default=False,
118
                            dest='disable_ip', help='disable IP module')
119
        parser.add_argument('--disable-diskio', action='store_true', default=False,
120
                            dest='disable_diskio', help='disable disk I/O module')
121
        parser.add_argument('--disable-fs', action='store_true', default=False,
122
                            dest='disable_fs', help='disable filesystem module')
123
        parser.add_argument('--disable-folder', action='store_true', default=False,
124
                            dest='disable_folder', help='disable folder module')
125
        parser.add_argument('--disable-sensors', action='store_true', default=False,
126
                            dest='disable_sensors', help='disable sensors module')
127
        parser.add_argument('--disable-hddtemp', action='store_true', default=False,
128
                            dest='disable_hddtemp', help='disable HD temperature module')
129
        parser.add_argument('--disable-raid', action='store_true', default=False,
130
                            dest='disable_raid', help='disable RAID module')
131
        parser.add_argument('--disable-docker', action='store_true', default=False,
132
                            dest='disable_docker', help='disable Docker module')
133
        parser.add_argument('-5', '--disable-top', action='store_true',
134
                            default=False, dest='disable_top',
135
                            help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
136
        parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
137
                            default=False, dest='disable_left_sidebar',
138
                            help='disable network, disk I/O, FS and sensors modules')
139
        parser.add_argument('--disable-process', action='store_true', default=False,
140
                            dest='disable_process', help='disable process module')
141
        parser.add_argument('--disable-log', action='store_true', default=False,
142
                            dest='disable_log', help='disable log module')
143
        parser.add_argument('--disable-bold', action='store_true', default=False,
144
                            dest='disable_bold', help='disable bold mode in the terminal')
145
        parser.add_argument('--disable-bg', action='store_true', default=False,
146
                            dest='disable_bg', help='disable background colors in the terminal')
147
        parser.add_argument('--enable-process-extended', action='store_true', default=False,
148
                            dest='enable_process_extended', help='enable extended stats on top process')
149
        parser.add_argument('--enable-history', action='store_true', default=False,
150
                            dest='enable_history', help='enable the history mode (matplotlib needed)')
151
        parser.add_argument('--path-history', default=tempfile.gettempdir(),
152
                            dest='path_history', help='set the export path for graph history')
153
        # Export modules feature
154
        parser.add_argument('--export-csv', default=None,
155
                            dest='export_csv', help='export stats to a CSV file')
156
        parser.add_argument('--export-influxdb', action='store_true', default=False,
157
                            dest='export_influxdb', help='export stats to an InfluxDB server (influxdb lib needed)')
158
        parser.add_argument('--export-opentsdb', action='store_true', default=False,
159
                            dest='export_opentsdb', help='export stats to an OpenTSDB server (potsdb lib needed)')
160
        parser.add_argument('--export-statsd', action='store_true', default=False,
161
                            dest='export_statsd', help='export stats to a StatsD server (statsd lib needed)')
162
        parser.add_argument('--export-elasticsearch', action='store_true', default=False,
163
                            dest='export_elasticsearch', help='export stats to an ElasticSearch server (elasticsearch lib needed)')
164
        parser.add_argument('--export-rabbitmq', action='store_true', default=False,
165
                            dest='export_rabbitmq', help='export stats to rabbitmq broker (pika lib needed)')
166
        parser.add_argument('--export-riemann', action='store_true', default=False,
167
                            dest='export_riemann', help='export stats to riemann broker (bernhard lib needed)')
168
        # Client/Server option
169
        parser.add_argument('-c', '--client', dest='client',
170
                            help='connect to a Glances server by IPv4/IPv6 address or hostname')
171
        parser.add_argument('-s', '--server', action='store_true', default=False,
172
                            dest='server', help='run Glances in server mode')
173
        parser.add_argument('--browser', action='store_true', default=False,
174
                            dest='browser', help='start the client browser (list of servers)')
175
        parser.add_argument('--disable-autodiscover', action='store_true', default=False,
176
                            dest='disable_autodiscover', help='disable autodiscover feature')
177
        parser.add_argument('-p', '--port', default=None, type=int, dest='port',
178
                            help='define the client/server TCP port [default: {0}]'.format(self.server_port))
179
        parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
180
                            help='bind server to the given IPv4/IPv6 address or hostname')
181
        parser.add_argument('--username', action='store_true', default=False, dest='username_prompt',
182
                            help='define a client/server username')
183
        parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
184
                            help='define a client/server password')
185
        parser.add_argument('--snmp-community', default='public', dest='snmp_community',
186
                            help='SNMP community')
187
        parser.add_argument('--snmp-port', default=161, type=int,
188
                            dest='snmp_port', help='SNMP port')
189
        parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
190
                            help='SNMP version (1, 2c or 3)')
191
        parser.add_argument('--snmp-user', default='private', dest='snmp_user',
192
                            help='SNMP username (only for SNMPv3)')
193
        parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
194
                            help='SNMP authentication key (only for SNMPv3)')
195
        parser.add_argument('--snmp-force', action='store_true', default=False,
196
                            dest='snmp_force', help='force SNMP mode')
197
        parser.add_argument('-t', '--time', default=self.refresh_time, type=float,
198
                            dest='time', help='set refresh time in seconds [default: {0} sec]'.format(self.refresh_time))
199
        parser.add_argument('-w', '--webserver', action='store_true', default=False,
200
                            dest='webserver', help='run Glances in web server mode (bottle needed)')
201
        # Display options
202
        parser.add_argument('-q', '--quiet', default=False, action='store_true',
203
                            dest='quiet', help='do not display the curses interface')
204
        parser.add_argument('-f', '--process-filter', default=None, type=str,
205
                            dest='process_filter', help='set the process filter pattern (regular expression)')
206
        parser.add_argument('--process-short-name', action='store_true', default=False,
207
                            dest='process_short_name', help='force short name for processes name')
208
        parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
209
                            dest='disable_irix', help='Task\'s cpu usage will be divided by the total number of CPUs')
210
        if not WINDOWS:
211
            parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
212
                                dest='no_kernel_threads', help='hide kernel threads in process list')
213
        if LINUX:
214
            parser.add_argument('--tree', action='store_true', default=False,
215
                                dest='process_tree', help='display processes as a tree')
216
        parser.add_argument('-b', '--byte', action='store_true', default=False,
217
                            dest='byte', help='display network rate in byte per second')
218
        parser.add_argument('--diskio-show-ramfs', action='store_true', default=False,
219
                            dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin')
220
        parser.add_argument('--diskio-iops', action='store_true', default=False,
221
                            dest='diskio_iops', help='show IO per second in the DiskIO plugin')
222
        parser.add_argument('--fahrenheit', action='store_true', default=False,
223
                            dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
224
        parser.add_argument('-1', '--percpu', action='store_true', default=False,
225
                            dest='percpu', help='start Glances in per CPU mode')
226
        parser.add_argument('--fs-free-space', action='store_true', default=False,
227
                            dest='fs_free_space', help='display FS free space instead of used')
228
        parser.add_argument('--theme-white', action='store_true', default=False,
229
                            dest='theme_white', help='optimize display colors for white background')
230
231
        return parser
232
233
    def parse_args(self):
234
        """Parse command line arguments."""
235
        args = self.init_args().parse_args()
236
237
        # Load the configuration file, if it exists
238
        self.config = Config(args.conf_file)
239
240
        # Debug mode
241
        if args.debug:
242
            from logging import DEBUG
243
            logger.setLevel(DEBUG)
244
245
        # Client/server Port
246
        if args.port is None:
247
            if args.webserver:
248
                args.port = self.web_server_port
249
            else:
250
                args.port = self.server_port
251
252
        # Autodiscover
253
        if args.disable_autodiscover:
254
            logger.info("Auto discover mode is disabled")
255
256
        # In web server mode
257
        if args.webserver:
258
            args.process_short_name = True
259
260
        # Server or client login/password
261
        if args.username_prompt:
262
            # Every username needs a password
263
            args.password_prompt = True
264
            # Prompt username
265
            if args.server:
266
                args.username = self.__get_username(description='Define the Glances server username: ')
267
            elif args.webserver:
268
                args.username = self.__get_username(description='Define the Glances webserver username: ')
269
            elif args.client:
270
                args.username = self.__get_username(description='Enter the Glances server username: ')
271
        else:
272
            # Default user name is 'glances'
273
            args.username = self.username
274
275
        if args.password_prompt:
276
            # Interactive or file password
277
            if args.server:
278
                args.password = self.__get_password(
279
                    description='Define the Glances server password ({0} username): '.format(args.username),
280
                    confirm=True,
281
                    username=args.username)
282
            elif args.webserver:
283
                args.password = self.__get_password(
284
                    description='Define the Glances webserver password ({0} username): '.format(args.username),
285
                    confirm=True,
286
                    username=args.username)
287
            elif args.client:
288
                args.password = self.__get_password(
289
                    description='Enter the Glances server password ({0} username): '.format(args.username),
290
                    clear=True,
291
                    username=args.username)
292
        else:
293
            # Default is no password
294
            args.password = self.password
295
296
        # By default help is hidden
297
        args.help_tag = False
298
299
        # Display Rx and Tx, not the sum for the network
300
        args.network_sum = False
301
        args.network_cumul = False
302
303
        # Manage full quicklook option
304
        if args.full_quicklook:
305
            logger.info("Disable QuickLook menu")
306
            args.disable_quicklook = False
307
            args.disable_cpu = True
308
            args.disable_mem = True
309
            args.disable_swap = True
310
            args.disable_load = False
311
312
        # Manage disable_top option
313
        if args.disable_top:
314
            logger.info("Disable top menu")
315
            args.disable_quicklook = True
316
            args.disable_cpu = True
317
            args.disable_mem = True
318
            args.disable_swap = True
319
            args.disable_load = True
320
321
        # Control parameter and exit if it is not OK
322
        self.args = args
323
324
        # Export is only available in standalone or client mode (issue #614)
325
        export_tag = args.export_csv or args.export_elasticsearch or args.export_statsd or args.export_influxdb or args.export_opentsdb or args.export_rabbitmq
326
        if not (self.is_standalone() or self.is_client()) and export_tag:
327
            logger.critical("Export is only available in standalone or client mode")
328
            sys.exit(2)
329
330
        # Filter is only available in standalone mode
331
        if args.process_filter is not None and not self.is_standalone():
332
            logger.critical("Process filter is only available in standalone mode")
333
            sys.exit(2)
334
335
        # Check graph output path
336
        if args.enable_history and args.path_history is not None:
337
            if not os.access(args.path_history, os.W_OK):
338
                logger.critical("History output path {0} do not exist or is not writable".format(args.path_history))
339
                sys.exit(2)
340
            logger.debug("History output path is set to {0}".format(args.path_history))
341
342
        # Disable HDDTemp if sensors are disabled
343
        if args.disable_sensors:
344
            args.disable_hddtemp = True
345
            logger.debug("Sensors and HDDTemp are disabled")
346
347
        return args
348
349
    def __get_username(self, description=''):
350
        """Read a username from the command line.
351
        """
352
        return input(description)
353
354
    def __get_password(self, description='', confirm=False, clear=False, username='glances'):
355
        """Read a password from the command line.
356
357
        - if confirm = True, with confirmation
358
        - if clear = True, plain (clear password)
359
        """
360
        from glances.password import GlancesPassword
361
        password = GlancesPassword(username=username)
362
        return password.get_password(description, confirm, clear)
363
364
    def is_standalone(self):
365
        """Return True if Glances is running in standalone mode."""
366
        return not self.args.client and not self.args.browser and not self.args.server and not self.args.webserver
367
368
    def is_client(self):
369
        """Return True if Glances is running in client mode."""
370
        return (self.args.client or self.args.browser) and not self.args.server
371
372
    def is_client_browser(self):
373
        """Return True if Glances is running in client browser mode."""
374
        return self.args.browser and not self.args.server
375
376
    def is_server(self):
377
        """Return True if Glances is running in server mode."""
378
        return not self.args.client and self.args.server
379
380
    def is_webserver(self):
381
        """Return True if Glances is running in Web server mode."""
382
        return not self.args.client and self.args.webserver
383
384
    def get_config(self):
385
        """Return configuration file object."""
386
        return self.config
387
388
    def get_args(self):
389
        """Return the arguments."""
390
        return self.args
391