1 | # -*- coding: utf-8 -*- |
||
2 | # |
||
3 | # This file is part of Glances. |
||
4 | # |
||
5 | # Copyright (C) 2019 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 sys |
||
24 | import tempfile |
||
25 | |||
26 | from glances import __version__, psutil_version |
||
27 | from glances.compat import input |
||
28 | from glances.config import Config |
||
29 | from glances.globals import WINDOWS |
||
30 | from glances.logger import logger |
||
31 | |||
32 | |||
33 | def disable(class_name, var): |
||
34 | """Set disable_<var> to True in the class class_name.""" |
||
35 | setattr(class_name, 'disable_' + var, True) |
||
36 | |||
37 | |||
38 | def enable(class_name, var): |
||
39 | """Set disable_<var> to False in the class class_name.""" |
||
40 | setattr(class_name, 'disable_' + var, False) |
||
41 | |||
42 | |||
43 | class GlancesMain(object): |
||
44 | """Main class to manage Glances instance.""" |
||
45 | |||
46 | # Default stats' refresh time is 3 seconds |
||
47 | refresh_time = 3 |
||
48 | # Set the default cache lifetime to 1 second (only for server) |
||
49 | cached_time = 1 |
||
50 | # By default, Glances is ran in standalone mode (no client/server) |
||
51 | client_tag = False |
||
52 | # Server TCP port number (default is 61209) |
||
53 | server_port = 61209 |
||
54 | # Web Server TCP port number (default is 61208) |
||
55 | web_server_port = 61208 |
||
56 | # Default username/password for client/server mode |
||
57 | username = "glances" |
||
58 | password = "" |
||
59 | |||
60 | # Examples of use |
||
61 | example_of_use = """ |
||
62 | Examples of use: |
||
63 | Monitor local machine (standalone mode): |
||
64 | $ glances |
||
65 | |||
66 | Display all Glances modules (plugins and exporters) and exit: |
||
67 | $ glances --module-list |
||
68 | |||
69 | Monitor local machine with the Web interface and start RESTful server: |
||
70 | $ glances -w |
||
71 | Glances web server started on http://0.0.0.0:61208/ |
||
72 | |||
73 | Only start RESTful API (without the WebUI): |
||
74 | $ glances -w --disable-webui |
||
75 | Glances API available on http://0.0.0.0:61208/api/ |
||
76 | |||
77 | Monitor local machine and export stats to a CSV file (standalone mode): |
||
78 | $ glances --export csv --export-csv-file /tmp/glances.csv |
||
79 | |||
80 | Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode): |
||
81 | $ glances -t 5 --export influxdb |
||
82 | |||
83 | Start a Glances XML/RCP server (server mode): |
||
84 | $ glances -s |
||
85 | |||
86 | Connect Glances to a Glances XML/RCP server (client mode): |
||
87 | $ glances -c <ip_server> |
||
88 | |||
89 | Connect Glances to a Glances server and export stats to a StatsD server (client mode): |
||
90 | $ glances -c <ip_server> --export statsd |
||
91 | |||
92 | Start the client browser (browser mode): |
||
93 | $ glances --browser |
||
94 | |||
95 | Display stats to stdout (one stat per line): |
||
96 | $ glances --stdout now,cpu.user,mem.used,load |
||
97 | |||
98 | Display CSV stats to stdout (all stats in one line): |
||
99 | $ glances --stdout-csv now,cpu.user,mem.used,load |
||
100 | |||
101 | Disable some plugins (any modes): |
||
102 | $ glances --disable-plugin network,ports |
||
103 | """ |
||
104 | |||
105 | def __init__(self): |
||
106 | """Manage the command line arguments.""" |
||
107 | # Read the command line arguments |
||
108 | self.args = self.parse_args() |
||
109 | |||
110 | def init_args(self): |
||
111 | """Init all the command line arguments.""" |
||
112 | version = "Glances v" + __version__ + " with psutil v" + psutil_version |
||
113 | parser = argparse.ArgumentParser( |
||
114 | prog='glances', |
||
115 | conflict_handler='resolve', |
||
116 | formatter_class=argparse.RawDescriptionHelpFormatter, |
||
117 | epilog=self.example_of_use) |
||
118 | parser.add_argument( |
||
119 | '-V', '--version', action='version', version=version) |
||
120 | parser.add_argument('-d', '--debug', action='store_true', default=False, |
||
121 | dest='debug', help='enable debug mode') |
||
122 | parser.add_argument('-C', '--config', dest='conf_file', |
||
123 | help='path to the configuration file') |
||
124 | # Disable plugin |
||
125 | parser.add_argument('--modules-list', '--module-list', |
||
126 | action='store_true', default=False, |
||
127 | dest='modules_list', |
||
128 | help='display modules (plugins & exports) list and exit') |
||
129 | parser.add_argument('--disable-plugin', dest='disable_plugin', |
||
130 | help='disable plugin (comma separed list)') |
||
131 | parser.add_argument('--disable-process', action='store_true', default=False, |
||
132 | dest='disable_process', help='disable process module') |
||
133 | # Enable or disable option |
||
134 | parser.add_argument('--disable-webui', action='store_true', default=False, |
||
135 | dest='disable_webui', help='disable the Web Interface') |
||
136 | parser.add_argument('--light', '--enable-light', action='store_true', |
||
137 | default=False, dest='enable_light', |
||
138 | help='light mode for Curses UI (disable all but top menu)') |
||
139 | parser.add_argument('-0', '--disable-irix', action='store_true', default=False, |
||
140 | dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs') |
||
141 | parser.add_argument('-1', '--percpu', action='store_true', default=False, |
||
142 | dest='percpu', help='start Glances in per CPU mode') |
||
143 | parser.add_argument('-2', '--disable-left-sidebar', action='store_true', |
||
144 | default=False, dest='disable_left_sidebar', |
||
145 | help='disable network, disk I/O, FS and sensors modules') |
||
146 | parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False, |
||
147 | dest='disable_quicklook', help='disable quick look module') |
||
148 | parser.add_argument('-4', '--full-quicklook', action='store_true', default=False, |
||
149 | dest='full_quicklook', help='disable all but quick look and load') |
||
150 | parser.add_argument('-5', '--disable-top', action='store_true', |
||
151 | default=False, dest='disable_top', |
||
152 | help='disable top menu (QL, CPU, MEM, SWAP and LOAD)') |
||
153 | parser.add_argument('-6', '--meangpu', action='store_true', default=False, |
||
154 | dest='meangpu', help='start Glances in mean GPU mode') |
||
155 | parser.add_argument('--disable-history', action='store_true', default=False, |
||
156 | dest='disable_history', help='disable stats history') |
||
157 | parser.add_argument('--disable-bold', action='store_true', default=False, |
||
158 | dest='disable_bold', help='disable bold mode in the terminal') |
||
159 | parser.add_argument('--disable-bg', action='store_true', default=False, |
||
160 | dest='disable_bg', help='disable background colors in the terminal') |
||
161 | parser.add_argument('--enable-irq', action='store_true', default=False, |
||
0 ignored issues
–
show
|
|||
162 | dest='enable_irq', help='enable IRQ module'), |
||
163 | parser.add_argument('--enable-process-extended', action='store_true', default=False, |
||
164 | dest='enable_process_extended', help='enable extended stats on top process') |
||
165 | # Export modules feature |
||
166 | parser.add_argument('--export', dest='export', |
||
167 | help='enable export module (comma separed list)') |
||
168 | parser.add_argument('--export-csv-file', |
||
169 | default='./glances.csv', |
||
170 | dest='export_csv_file', |
||
171 | help='file path for CSV exporter') |
||
172 | parser.add_argument('--export-csv-overwrite', action='store_true', default=False, |
||
173 | dest='export_csv_overwrite', help='overwrite existing CSV file') |
||
174 | parser.add_argument('--export-json-file', |
||
175 | default='./glances.json', |
||
176 | dest='export_json_file', |
||
177 | help='file path for JSON exporter') |
||
178 | parser.add_argument('--export-graph-path', |
||
179 | default=tempfile.gettempdir(), |
||
180 | dest='export_graph_path', |
||
181 | help='Folder for Graph exporter') |
||
182 | # Client/Server option |
||
183 | parser.add_argument('-c', '--client', dest='client', |
||
184 | help='connect to a Glances server by IPv4/IPv6 address or hostname') |
||
185 | parser.add_argument('-s', '--server', action='store_true', default=False, |
||
186 | dest='server', help='run Glances in server mode') |
||
187 | parser.add_argument('--browser', action='store_true', default=False, |
||
188 | dest='browser', help='start the client browser (list of servers)') |
||
189 | parser.add_argument('--disable-autodiscover', action='store_true', default=False, |
||
190 | dest='disable_autodiscover', help='disable autodiscover feature') |
||
191 | parser.add_argument('-p', '--port', default=None, type=int, dest='port', |
||
192 | help='define the client/server TCP port [default: {}]'.format(self.server_port)) |
||
193 | parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address', |
||
194 | help='bind server to the given IPv4/IPv6 address or hostname') |
||
195 | parser.add_argument('--username', action='store_true', default=False, dest='username_prompt', |
||
196 | help='define a client/server username') |
||
197 | parser.add_argument('--password', action='store_true', default=False, dest='password_prompt', |
||
198 | help='define a client/server password') |
||
199 | parser.add_argument('-u', dest='username_used', |
||
200 | help='use the given client/server username') |
||
201 | parser.add_argument('--snmp-community', default='public', dest='snmp_community', |
||
202 | help='SNMP community') |
||
203 | parser.add_argument('--snmp-port', default=161, type=int, |
||
204 | dest='snmp_port', help='SNMP port') |
||
205 | parser.add_argument('--snmp-version', default='2c', dest='snmp_version', |
||
206 | help='SNMP version (1, 2c or 3)') |
||
207 | parser.add_argument('--snmp-user', default='private', dest='snmp_user', |
||
208 | help='SNMP username (only for SNMPv3)') |
||
209 | parser.add_argument('--snmp-auth', default='password', dest='snmp_auth', |
||
210 | help='SNMP authentication key (only for SNMPv3)') |
||
211 | parser.add_argument('--snmp-force', action='store_true', default=False, |
||
212 | dest='snmp_force', help='force SNMP mode') |
||
213 | parser.add_argument('-t', '--time', default=self.refresh_time, type=float, |
||
214 | dest='time', help='set refresh time in seconds [default: {} sec]'.format(self.refresh_time)) |
||
215 | parser.add_argument('-w', '--webserver', action='store_true', default=False, |
||
216 | dest='webserver', help='run Glances in web server mode (bottle needed)') |
||
217 | parser.add_argument('--cached-time', default=self.cached_time, type=int, |
||
218 | dest='cached_time', help='set the server cache time [default: {} sec]'.format(self.cached_time)) |
||
219 | parser.add_argument('--open-web-browser', action='store_true', default=False, |
||
220 | dest='open_web_browser', help='try to open the Web UI in the default Web browser') |
||
221 | # Display options |
||
222 | parser.add_argument('-q', '--quiet', default=False, action='store_true', |
||
223 | dest='quiet', help='do not display the curses interface') |
||
224 | parser.add_argument('-f', '--process-filter', default=None, type=str, |
||
225 | dest='process_filter', help='set the process filter pattern (regular expression)') |
||
226 | parser.add_argument('--process-short-name', action='store_true', default=False, |
||
227 | dest='process_short_name', help='force short name for processes name') |
||
228 | parser.add_argument('--stdout', default=None, |
||
229 | dest='stdout', help='display stats to stdout, one stat per line (comma separated list of plugins/plugins.attribute)') |
||
230 | parser.add_argument('--stdout-csv', default=None, |
||
231 | dest='stdout_csv', help='display stats to stdout, csv format (comma separated list of plugins/plugins.attribute)') |
||
232 | if not WINDOWS: |
||
233 | parser.add_argument('--hide-kernel-threads', action='store_true', default=False, |
||
234 | dest='no_kernel_threads', help='hide kernel threads in process list (not available on Windows)') |
||
235 | parser.add_argument('-b', '--byte', action='store_true', default=False, |
||
236 | dest='byte', help='display network rate in byte per second') |
||
237 | parser.add_argument('--diskio-show-ramfs', action='store_true', default=False, |
||
238 | dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin') |
||
239 | parser.add_argument('--diskio-iops', action='store_true', default=False, |
||
240 | dest='diskio_iops', help='show IO per second in the DiskIO plugin') |
||
241 | parser.add_argument('--fahrenheit', action='store_true', default=False, |
||
242 | dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)') |
||
243 | parser.add_argument('--fs-free-space', action='store_true', default=False, |
||
244 | dest='fs_free_space', help='display FS free space instead of used') |
||
245 | parser.add_argument('--sparkline', action='store_true', default=False, |
||
246 | dest='sparkline', help='display sparklines instead of bar in the curses interface') |
||
247 | parser.add_argument('--theme-white', action='store_true', default=False, |
||
248 | dest='theme_white', help='optimize display colors for white background') |
||
249 | # Globals options |
||
250 | parser.add_argument('--disable-check-update', action='store_true', default=False, |
||
251 | dest='disable_check_update', help='disable online Glances version ckeck') |
||
252 | return parser |
||
253 | |||
254 | def parse_args(self): |
||
255 | """Parse command line arguments.""" |
||
256 | args = self.init_args().parse_args() |
||
257 | |||
258 | # Load the configuration file, if it exists |
||
259 | self.config = Config(args.conf_file) |
||
260 | |||
261 | # Debug mode |
||
262 | if args.debug: |
||
263 | from logging import DEBUG |
||
264 | logger.setLevel(DEBUG) |
||
265 | else: |
||
266 | from warnings import simplefilter |
||
267 | simplefilter("ignore") |
||
268 | |||
269 | # Plugins disable/enable |
||
270 | if args.disable_plugin is not None: |
||
271 | for p in args.disable_plugin.split(','): |
||
272 | disable(args, p) |
||
273 | else: |
||
274 | # Allow users to disable plugins from the glances.conf (issue #1378) |
||
275 | for s in self.config.sections(): |
||
276 | if self.config.has_section(s) \ |
||
277 | and (self.config.get_bool_value(s, 'disable', False)): |
||
278 | disable(args, s) |
||
279 | logger.debug('{} disabled by the configuration file'.format(s)) |
||
280 | |||
281 | # Exporters activation |
||
282 | if args.export is not None: |
||
283 | for p in args.export.split(','): |
||
284 | setattr(args, 'export_' + p, True) |
||
285 | |||
286 | # Client/server Port |
||
287 | if args.port is None: |
||
288 | if args.webserver: |
||
289 | args.port = self.web_server_port |
||
290 | else: |
||
291 | args.port = self.server_port |
||
292 | # Port in the -c URI #996 |
||
293 | if args.client is not None: |
||
294 | args.client, args.port = (x if x else y for (x, y) in zip(args.client.partition(':')[::2], (args.client, args.port))) |
||
295 | |||
296 | # Autodiscover |
||
297 | if args.disable_autodiscover: |
||
298 | logger.info("Auto discover mode is disabled") |
||
299 | |||
300 | # By default Windows is started in Web mode |
||
301 | if WINDOWS: |
||
302 | args.webserver = True |
||
303 | |||
304 | # In web server mode, default refresh time: 5 sec |
||
305 | if args.webserver: |
||
306 | args.time = 5 |
||
307 | args.process_short_name = True |
||
308 | |||
309 | # Server or client login/password |
||
310 | if args.username_prompt: |
||
311 | # Every username needs a password |
||
312 | args.password_prompt = True |
||
313 | # Prompt username |
||
314 | if args.server: |
||
315 | args.username = self.__get_username( |
||
316 | description='Define the Glances server username: ') |
||
317 | elif args.webserver: |
||
318 | args.username = self.__get_username( |
||
319 | description='Define the Glances webserver username: ') |
||
320 | elif args.client: |
||
321 | args.username = self.__get_username( |
||
322 | description='Enter the Glances server username: ') |
||
323 | else: |
||
324 | if args.username_used: |
||
325 | # A username has been set using the -u option ? |
||
326 | args.username = args.username_used |
||
327 | else: |
||
328 | # Default user name is 'glances' |
||
329 | args.username = self.username |
||
330 | |||
331 | if args.password_prompt or args.username_used: |
||
332 | # Interactive or file password |
||
333 | if args.server: |
||
334 | args.password = self.__get_password( |
||
335 | description='Define the Glances server password ({} username): '.format( |
||
336 | args.username), |
||
337 | confirm=True, |
||
338 | username=args.username) |
||
339 | elif args.webserver: |
||
340 | args.password = self.__get_password( |
||
341 | description='Define the Glances webserver password ({} username): '.format( |
||
342 | args.username), |
||
343 | confirm=True, |
||
344 | username=args.username) |
||
345 | elif args.client: |
||
346 | args.password = self.__get_password( |
||
347 | description='Enter the Glances server password ({} username): '.format( |
||
348 | args.username), |
||
349 | clear=True, |
||
350 | username=args.username) |
||
351 | else: |
||
352 | # Default is no password |
||
353 | args.password = self.password |
||
354 | |||
355 | # By default help is hidden |
||
356 | args.help_tag = False |
||
357 | |||
358 | # Display Rx and Tx, not the sum for the network |
||
359 | args.network_sum = False |
||
360 | args.network_cumul = False |
||
361 | |||
362 | # Manage light mode |
||
363 | if args.enable_light: |
||
364 | logger.info("Light mode is on") |
||
365 | args.disable_left_sidebar = True |
||
366 | disable(args, 'process') |
||
367 | disable(args, 'alert') |
||
368 | disable(args, 'amps') |
||
369 | disable(args, 'docker') |
||
370 | |||
371 | # Manage full quicklook option |
||
372 | if args.full_quicklook: |
||
373 | logger.info("Full quicklook mode") |
||
374 | enable(args, 'quicklook') |
||
375 | disable(args, 'cpu') |
||
376 | disable(args, 'mem') |
||
377 | disable(args, 'memswap') |
||
378 | enable(args, 'load') |
||
379 | |||
380 | # Manage disable_top option |
||
381 | if args.disable_top: |
||
382 | logger.info("Disable top menu") |
||
383 | disable(args, 'quicklook') |
||
384 | disable(args, 'cpu') |
||
385 | disable(args, 'mem') |
||
386 | disable(args, 'memswap') |
||
387 | disable(args, 'load') |
||
388 | |||
389 | # Init the generate_graph tag |
||
390 | # Should be set to True to generate graphs |
||
391 | args.generate_graph = False |
||
392 | |||
393 | # Control parameter and exit if it is not OK |
||
394 | self.args = args |
||
395 | |||
396 | # Export is only available in standalone or client mode (issue #614) |
||
397 | export_tag = self.args.export is not None and any(self.args.export) |
||
398 | if WINDOWS and export_tag: |
||
399 | # On Windows, export is possible but only in quiet mode |
||
400 | # See issue #1038 |
||
401 | logger.info("On Windows OS, export disable the Web interface") |
||
402 | self.args.quiet = True |
||
403 | self.args.webserver = False |
||
404 | elif not (self.is_standalone() or self.is_client()) and export_tag: |
||
405 | logger.critical("Export is only available in standalone or client mode") |
||
406 | sys.exit(2) |
||
407 | |||
408 | # Filter is only available in standalone mode |
||
409 | if args.process_filter is not None and not self.is_standalone(): |
||
410 | logger.critical( |
||
411 | "Process filter is only available in standalone mode") |
||
412 | sys.exit(2) |
||
413 | |||
414 | # Disable HDDTemp if sensors are disabled |
||
415 | if getattr(args, 'disable_sensors', False): |
||
416 | disable(args, 'hddtemp') |
||
417 | logger.debug("Sensors and HDDTemp are disabled") |
||
418 | |||
419 | return args |
||
420 | |||
421 | def is_standalone(self): |
||
422 | """Return True if Glances is running in standalone mode.""" |
||
423 | return (not self.args.client and |
||
424 | not self.args.browser and |
||
425 | not self.args.server and |
||
426 | not self.args.webserver) |
||
427 | |||
428 | def is_client(self): |
||
429 | """Return True if Glances is running in client mode.""" |
||
430 | return (self.args.client or self.args.browser) and not self.args.server |
||
431 | |||
432 | def is_client_browser(self): |
||
433 | """Return True if Glances is running in client browser mode.""" |
||
434 | return self.args.browser and not self.args.server |
||
435 | |||
436 | def is_server(self): |
||
437 | """Return True if Glances is running in server mode.""" |
||
438 | return not self.args.client and self.args.server |
||
439 | |||
440 | def is_webserver(self): |
||
441 | """Return True if Glances is running in Web server mode.""" |
||
442 | return not self.args.client and self.args.webserver |
||
443 | |||
444 | def get_config(self): |
||
445 | """Return configuration file object.""" |
||
446 | return self.config |
||
447 | |||
448 | def get_args(self): |
||
449 | """Return the arguments.""" |
||
450 | return self.args |
||
451 | |||
452 | def get_mode(self): |
||
453 | """Return the mode.""" |
||
454 | return self.mode |
||
455 | |||
456 | def __get_username(self, description=''): |
||
457 | """Read an username from the command line.""" |
||
458 | return input(description) |
||
459 | |||
460 | def __get_password(self, description='', |
||
461 | confirm=False, clear=False, username='glances'): |
||
462 | """Read a password from the command line. |
||
463 | |||
464 | - if confirm = True, with confirmation |
||
465 | - if clear = True, plain (clear password) |
||
466 | """ |
||
467 | from glances.password import GlancesPassword |
||
468 | password = GlancesPassword(username=username) |
||
469 | return password.get_password(description, confirm, clear) |
||
470 |
If a expression has no sideeffects (any lasting effect after it has been called) and its return value is not used, this usually means that this code can be removed or that an assignment is missing.