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
|
|
|
|