From 0fe2b71a356d859116009171f3114dbd184a7b11 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Mon, 19 Dec 2016 17:23:10 +0100 Subject: [PATCH 01/20] GPU plugin: dos -> unix line endings (3rd) --- glances/plugins/glances_gpu.py | 476 ++++++++++++++++----------------- 1 file changed, 238 insertions(+), 238 deletions(-) diff --git a/glances/plugins/glances_gpu.py b/glances/plugins/glances_gpu.py index 51eedd02..b788020a 100644 --- a/glances/plugins/glances_gpu.py +++ b/glances/plugins/glances_gpu.py @@ -1,239 +1,239 @@ # -*- coding: utf-8 -*- -# -# This file is part of Glances. -# -# Copyright (C) 2016 Kirby Banman -# -# Glances is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Glances is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -"""GPU plugin (limited to NVIDIA chipsets)""" - -from glances.logger import logger -from glances.plugins.glances_plugin import GlancesPlugin - -try: - import pynvml -except ImportError: - logger.debug("Could not import pynvml. NVIDIA stats will not be collected.") - gpu_nvidia_tag = False -else: - gpu_nvidia_tag = True - - -class Plugin(GlancesPlugin): - - """Glances GPU plugin (limited to NVIDIA chipsets). - - stats is a list of dictionaries with one entry per GPU - """ - - def __init__(self, args=None): - """Init the plugin""" - super(Plugin, self).__init__(args=args) - - # Init the NVidia API - self.init_nvidia() - - # We want to display the stat in the curse interface - self.display_curse = True - - # Init the stats - self.reset() - - def reset(self): - """Reset/init the stats.""" - self.stats = [] - - def init_nvidia(self): - """Init the NVIDIA API""" - if not gpu_nvidia_tag: - self.nvml_ready = False - - try: - pynvml.nvmlInit() - self.device_handles = self.get_device_handles() - self.nvml_ready = True - except Exception: - logger.debug("pynvml could not be initialized.") - self.nvml_ready = False - - return self.nvml_ready - - def get_key(self): - """Return the key of the list.""" - return 'gpu_id' - - @GlancesPlugin._check_decorator - @GlancesPlugin._log_result_decorator - def update(self): - """Update the GPU stats""" - - self.reset() - - # !!! JUST FOR TEST - # self.stats = [{"key": "gpu_id", "mem": None, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}] - # self.stats = [{"key": "gpu_id", "mem": 30, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}, - # {"key": "gpu_id", "mem": 70, "proc": 80, "gpu_id": 1, "name": "GeForce GTX 560 Ti"}] - # !!! TO BE REMOVED - - if not self.nvml_ready: - return self.stats - - if self.input_method == 'local': - self.stats = self.get_device_stats() - elif self.input_method == 'snmp': - # not available - pass - - return self.stats - - def update_views(self): - """Update stats views.""" - # Call the father's method - super(Plugin, self).update_views() - - # Add specifics informations - # Alert - for i in self.stats: - # Init the views for the current GPU - self.views[i[self.get_key()]] = {'proc': {}, 'mem': {}} - # Processor alert - if 'proc' in i: - alert = self.get_alert(i['proc'], header='proc') - self.views[i[self.get_key()]]['proc']['decoration'] = alert - # Memory alert - if 'mem' in i: - alert = self.get_alert(i['mem'], header='mem') - self.views[i[self.get_key()]]['mem']['decoration'] = alert - - return True - - def msg_curse(self, args=None): - """Return the dict to display in the curse interface.""" - # Init the return message - ret = [] - - # Only process if stats exist, not empty (issue #871) and plugin not disabled - if not self.stats or (self.stats == []) or self.is_disable(): - return ret - - # Build the string message - if len(self.stats) == 1: - # Mono GPU - gpu_stats = self.stats[0] - # Header - header = '{} {}'.format('GPU', gpu_stats['name']) - msg = header[:16] - ret.append(self.curse_add_line(msg, "TITLE")) - # New line - ret.append(self.curse_new_line()) - # GPU CPU - msg = '{:8}'.format('proc:') - ret.append(self.curse_add_line(msg)) - msg = '{:>7d}%'.format(int(gpu_stats['proc'])) - ret.append(self.curse_add_line( - msg, self.get_views(item=gpu_stats[self.get_key()], - key='proc', - option='decoration'))) - # New line - ret.append(self.curse_new_line()) - # GPU MEM - msg = '{:8}'.format('mem:') - ret.append(self.curse_add_line(msg)) - if gpu_stats['mem'] is None: - msg = '{:>8}'.format('N/A') - else: - msg = '{:>7d}%'.format(int(gpu_stats['mem'])) - ret.append(self.curse_add_line( - msg, self.get_views(item=gpu_stats[self.get_key()], - key='mem', - option='decoration'))) - else: - # Multi GPU - # Header - header = '{} {}'.format(len(self.stats), 'GPUs') - msg = header[:16] - ret.append(self.curse_add_line(msg, "TITLE")) - for gpu_stats in self.stats: - # New line - ret.append(self.curse_new_line()) - # GPU ID + PROC + MEM - msg = '{}: {:>3}% mem: {:>3}%'.format(gpu_stats['gpu_id'], - gpu_stats['proc'], - gpu_stats['mem'],) - ret.append(self.curse_add_line(msg)) - - return ret - - def get_device_handles(self): - """ - Returns a list of NVML device handles, one per device. Can throw NVMLError. - """ - return [pynvml.nvmlDeviceGetHandleByIndex(i) for i in range(0, pynvml.nvmlDeviceGetCount())] - - def get_device_stats(self): - """Get GPU stats""" - stats = [] - - for index, device_handle in enumerate(self.device_handles): - device_stats = {} - # Dictionnary key is the GPU_ID - device_stats['key'] = self.get_key() - # GPU id (for multiple GPU, start at 0) - device_stats['gpu_id'] = index - # GPU name - device_stats['name'] = self.get_device_name(device_handle) - # Memory consumption in % (not available on all GPU) - device_stats['mem'] = self.get_mem(device_handle) - # Processor consumption in % - device_stats['proc'] = self.get_proc(device_handle) - stats.append(device_stats) - - return stats - - def get_device_name(self, device_handle): - """Get GPU device name""" - try: - return pynvml.nvmlDeviceGetName(device_handle) - except pynvml.NVMlError: - return "NVIDIA GPU" - - def get_mem(self, device_handle): - """Get GPU device memory consumption in percent""" - try: - return pynvml.nvmlDeviceGetUtilizationRates(device_handle).memory - except pynvml.NVMLError: - try: - memory_info = pynvml.nvmlDeviceGetMemoryInfo(device_handle) - return memory_info.used * 100 / memory_info.total - except pynvml.NVMLError: - return None - - def get_proc(self, device_handle): - """Get GPU device CPU consumption in percent""" - try: - return pynvml.nvmlDeviceGetUtilizationRates(device_handle).gpu - except pynvml.NVMLError: - return None - - def exit(self): - """Overwrite the exit method to close the GPU API""" - if self.nvml_ready: - try: - pynvml.nvmlShutdown() - except Exception as e: - logger.debug("pynvml failed to shutdown correctly ({})".format(e)) - - # Call the father exit method - super(Plugin, self).exit() +# +# This file is part of Glances. +# +# Copyright (C) 2016 Kirby Banman +# +# Glances is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Glances is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +"""GPU plugin (limited to NVIDIA chipsets)""" + +from glances.logger import logger +from glances.plugins.glances_plugin import GlancesPlugin + +try: + import pynvml +except ImportError: + logger.debug("Could not import pynvml. NVIDIA stats will not be collected.") + gpu_nvidia_tag = False +else: + gpu_nvidia_tag = True + + +class Plugin(GlancesPlugin): + + """Glances GPU plugin (limited to NVIDIA chipsets). + + stats is a list of dictionaries with one entry per GPU + """ + + def __init__(self, args=None): + """Init the plugin""" + super(Plugin, self).__init__(args=args) + + # Init the NVidia API + self.init_nvidia() + + # We want to display the stat in the curse interface + self.display_curse = True + + # Init the stats + self.reset() + + def reset(self): + """Reset/init the stats.""" + self.stats = [] + + def init_nvidia(self): + """Init the NVIDIA API""" + if not gpu_nvidia_tag: + self.nvml_ready = False + + try: + pynvml.nvmlInit() + self.device_handles = self.get_device_handles() + self.nvml_ready = True + except Exception: + logger.debug("pynvml could not be initialized.") + self.nvml_ready = False + + return self.nvml_ready + + def get_key(self): + """Return the key of the list.""" + return 'gpu_id' + + @GlancesPlugin._check_decorator + @GlancesPlugin._log_result_decorator + def update(self): + """Update the GPU stats""" + + self.reset() + + # !!! JUST FOR TEST + # self.stats = [{"key": "gpu_id", "mem": None, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}] + # self.stats = [{"key": "gpu_id", "mem": 30, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}, + # {"key": "gpu_id", "mem": 70, "proc": 80, "gpu_id": 1, "name": "GeForce GTX 560 Ti"}] + # !!! TO BE REMOVED + + if not self.nvml_ready: + return self.stats + + if self.input_method == 'local': + self.stats = self.get_device_stats() + elif self.input_method == 'snmp': + # not available + pass + + return self.stats + + def update_views(self): + """Update stats views.""" + # Call the father's method + super(Plugin, self).update_views() + + # Add specifics informations + # Alert + for i in self.stats: + # Init the views for the current GPU + self.views[i[self.get_key()]] = {'proc': {}, 'mem': {}} + # Processor alert + if 'proc' in i: + alert = self.get_alert(i['proc'], header='proc') + self.views[i[self.get_key()]]['proc']['decoration'] = alert + # Memory alert + if 'mem' in i: + alert = self.get_alert(i['mem'], header='mem') + self.views[i[self.get_key()]]['mem']['decoration'] = alert + + return True + + def msg_curse(self, args=None): + """Return the dict to display in the curse interface.""" + # Init the return message + ret = [] + + # Only process if stats exist, not empty (issue #871) and plugin not disabled + if not self.stats or (self.stats == []) or self.is_disable(): + return ret + + # Build the string message + if len(self.stats) == 1: + # Mono GPU + gpu_stats = self.stats[0] + # Header + header = '{} {}'.format('GPU', gpu_stats['name']) + msg = header[:16] + ret.append(self.curse_add_line(msg, "TITLE")) + # New line + ret.append(self.curse_new_line()) + # GPU CPU + msg = '{:8}'.format('proc:') + ret.append(self.curse_add_line(msg)) + msg = '{:>7d}%'.format(int(gpu_stats['proc'])) + ret.append(self.curse_add_line( + msg, self.get_views(item=gpu_stats[self.get_key()], + key='proc', + option='decoration'))) + # New line + ret.append(self.curse_new_line()) + # GPU MEM + msg = '{:8}'.format('mem:') + ret.append(self.curse_add_line(msg)) + if gpu_stats['mem'] is None: + msg = '{:>8}'.format('N/A') + else: + msg = '{:>7d}%'.format(int(gpu_stats['mem'])) + ret.append(self.curse_add_line( + msg, self.get_views(item=gpu_stats[self.get_key()], + key='mem', + option='decoration'))) + else: + # Multi GPU + # Header + header = '{} {}'.format(len(self.stats), 'GPUs') + msg = header[:16] + ret.append(self.curse_add_line(msg, "TITLE")) + for gpu_stats in self.stats: + # New line + ret.append(self.curse_new_line()) + # GPU ID + PROC + MEM + msg = '{}: {:>3}% mem: {:>3}%'.format(gpu_stats['gpu_id'], + gpu_stats['proc'], + gpu_stats['mem'],) + ret.append(self.curse_add_line(msg)) + + return ret + + def get_device_handles(self): + """ + Returns a list of NVML device handles, one per device. Can throw NVMLError. + """ + return [pynvml.nvmlDeviceGetHandleByIndex(i) for i in range(0, pynvml.nvmlDeviceGetCount())] + + def get_device_stats(self): + """Get GPU stats""" + stats = [] + + for index, device_handle in enumerate(self.device_handles): + device_stats = {} + # Dictionnary key is the GPU_ID + device_stats['key'] = self.get_key() + # GPU id (for multiple GPU, start at 0) + device_stats['gpu_id'] = index + # GPU name + device_stats['name'] = self.get_device_name(device_handle) + # Memory consumption in % (not available on all GPU) + device_stats['mem'] = self.get_mem(device_handle) + # Processor consumption in % + device_stats['proc'] = self.get_proc(device_handle) + stats.append(device_stats) + + return stats + + def get_device_name(self, device_handle): + """Get GPU device name""" + try: + return pynvml.nvmlDeviceGetName(device_handle) + except pynvml.NVMlError: + return "NVIDIA GPU" + + def get_mem(self, device_handle): + """Get GPU device memory consumption in percent""" + try: + return pynvml.nvmlDeviceGetUtilizationRates(device_handle).memory + except pynvml.NVMLError: + try: + memory_info = pynvml.nvmlDeviceGetMemoryInfo(device_handle) + return memory_info.used * 100 / memory_info.total + except pynvml.NVMLError: + return None + + def get_proc(self, device_handle): + """Get GPU device CPU consumption in percent""" + try: + return pynvml.nvmlDeviceGetUtilizationRates(device_handle).gpu + except pynvml.NVMLError: + return None + + def exit(self): + """Overwrite the exit method to close the GPU API""" + if self.nvml_ready: + try: + pynvml.nvmlShutdown() + except Exception as e: + logger.debug("pynvml failed to shutdown correctly ({})".format(e)) + + # Call the father exit method + super(Plugin, self).exit() From c2cb4b8a7f131295c9df74843267022fdab2148d Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Mon, 19 Dec 2016 19:50:02 +0100 Subject: [PATCH 02/20] setup.py: use 'with' context manager to open files --- setup.py | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/setup.py b/setup.py index 6241351a..4ca8722d 100755 --- a/setup.py +++ b/setup.py @@ -1,25 +1,30 @@ #!/usr/bin/env python import glob -import sys +import os import re +import sys +from io import open from setuptools import setup, Command +if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 3): + print('Glances requires at least Python 2.7 or 3.3 to run.') + sys.exit(1) + + # Global functions ################## -def get_version(): - """Get version inside the __init__.py file""" - init_file = open("glances/__init__.py").read() - reg_version = r"^__version__ = ['\"]([^'\"]*)['\"]" - find_version = re.search(reg_version, init_file, re.M) - if find_version: - return find_version.group(1) - else: - print("Can not retreive Glances version in the glances/__init__.py file.") - sys.exit(1) +with open(os.path.join('glances', '__init__.py'), encoding='utf-8') as f: + version = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", f.read(), re.M).group(1) + +if not version: + raise RuntimeError('Cannot find Glances version information.') + +with open('README.rst', encoding='utf-8') as f: + long_description = f.read() def get_data_files(): @@ -56,23 +61,14 @@ class tests(Command): raise SystemExit(ret) raise SystemExit(0) -# Global vars -############# - -glances_version = get_version() - -if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 3): - print('Glances {} require at least Python 2.7 or 3.3 to run.'.format(glances_version)) - print('Please install Glances 2.6.2 on your system.') - sys.exit(1) # Setup ! setup( name='Glances', - version=glances_version, + version=version, description="A cross-platform curses-based monitoring tool", - long_description=open('README.rst').read(), + long_description=long_description, author='Nicolas Hennion', author_email='nicolas@nicolargo.com', url='https://github.com/nicolargo/glances', From 1435f6ce0ce7491af7e5771d1fec158109ad69d9 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 11:23:20 +0100 Subject: [PATCH 03/20] Fix bad format string --- glances/plugins/glances_ports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/plugins/glances_ports.py b/glances/plugins/glances_ports.py index 13110bdd..47f2cfb0 100644 --- a/glances/plugins/glances_ports.py +++ b/glances/plugins/glances_ports.py @@ -251,7 +251,7 @@ class ThreadScanner(threading.Thread): try: ret = _socket.connect_ex((ip, int(port['port']))) except Exception as e: - logger.debug("0}: Error while scanning port {} ({})".format(self.plugin_name, port, e)) + logger.debug("{}: Error while scanning port {} ({})".format(self.plugin_name, port, e)) else: if ret == 0: port['status'] = counter.get() From bb01387e862db68ffec750e0d4a8549d2f709862 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 12:46:31 +0100 Subject: [PATCH 04/20] Cleanup imports - Fix wrong import order - Remove unused imports --- glances/exports/glances_cassandra.py | 2 +- glances/outputs/glances_bottle.py | 5 +---- glances/plugins/glances_irq.py | 1 - glances/plugins/glances_wifi.py | 2 -- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/glances/exports/glances_cassandra.py b/glances/exports/glances_cassandra.py index 79340528..0ac83386 100644 --- a/glances/exports/glances_cassandra.py +++ b/glances/exports/glances_cassandra.py @@ -20,6 +20,7 @@ """Cassandra/Scylla interface class.""" import sys +from datetime import datetime from numbers import Number from glances.compat import NoOptionError, NoSectionError @@ -29,7 +30,6 @@ from glances.exports.glances_export import GlancesExport from cassandra.cluster import Cluster from cassandra.util import uuid_from_time from cassandra import InvalidRequest -from datetime import datetime class Export(GlancesExport): diff --git a/glances/outputs/glances_bottle.py b/glances/outputs/glances_bottle.py index d5ee9e44..8a5d5896 100644 --- a/glances/outputs/glances_bottle.py +++ b/glances/outputs/glances_bottle.py @@ -27,7 +27,6 @@ from io import open import webbrowser from glances.timer import Timer -from glances.globals import WINDOWS from glances.logger import logger try: @@ -134,9 +133,7 @@ class GlancesBottle(object): # Try to open the Glances Web UI in the default Web browser if: # 1) --open-web-browser option is used # 2) Glances standalone mode is running on Windows OS - webbrowser.open(bindurl, - new=2, - autoraise=1) + webbrowser.open(bindurl, new=2, autoraise=1) self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug) def end(self): diff --git a/glances/plugins/glances_irq.py b/glances/plugins/glances_irq.py index 64d31f4c..2f4568f7 100644 --- a/glances/plugins/glances_irq.py +++ b/glances/plugins/glances_irq.py @@ -22,7 +22,6 @@ import os import operator -from glances.logger import logger from glances.globals import LINUX from glances.timer import getTimeSinceLastUpdate from glances.plugins.glances_plugin import GlancesPlugin diff --git a/glances/plugins/glances_wifi.py b/glances/plugins/glances_wifi.py index 8b91091b..fd5921d5 100644 --- a/glances/plugins/glances_wifi.py +++ b/glances/plugins/glances_wifi.py @@ -19,11 +19,9 @@ """Wifi plugin.""" -import base64 import operator from glances.logger import logger -from glances.timer import getTimeSinceLastUpdate from glances.plugins.glances_plugin import GlancesPlugin import psutil From 74fa804e94d5404dea3ca15cbada25c7ee495cba Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 12:50:52 +0100 Subject: [PATCH 05/20] Fix wrong continued indentation --- glances/plugins/glances_amps.py | 3 +-- glances/processes.py | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/glances/plugins/glances_amps.py b/glances/plugins/glances_amps.py index 637b48f5..9ea047c3 100644 --- a/glances/plugins/glances_amps.py +++ b/glances/plugins/glances_amps.py @@ -64,8 +64,7 @@ class Plugin(GlancesPlugin): 'timer': v.time_until_refresh(), 'count': v.count(), 'countmin': v.count_min(), - 'countmax': v.count_max(), - }) + 'countmax': v.count_max()}) else: # Not available in SNMP mode pass diff --git a/glances/processes.py b/glances/processes.py index b04ba2e5..a316be98 100644 --- a/glances/processes.py +++ b/glances/processes.py @@ -477,9 +477,9 @@ class GlancesProcesses(object): # ignore the 'kernel_task' process on OS X # waiting for upstream patch from psutil if (s is None or - BSD and s['name'] == 'idle' or - WINDOWS and s['name'] == 'System Idle Process' or - OSX and s['name'] == 'kernel_task'): + BSD and s['name'] == 'idle' or + WINDOWS and s['name'] == 'System Idle Process' or + OSX and s['name'] == 'kernel_task'): continue # Continue to the next process if it has to be filtered if self._filter.is_filtered(s): From 002510f19cee0fa9ee9bcb8332a957b6692ae560 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 12:58:50 +0100 Subject: [PATCH 06/20] Remove unused variable 'd' --- glances/attribute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/attribute.py b/glances/attribute.py index 60035ada..e38dda27 100644 --- a/glances/attribute.py +++ b/glances/attribute.py @@ -133,5 +133,5 @@ class GlancesAttribute(object): def history_mean(self, nb=5): """Return the mean on the values in the history. """ - d, v = zip(*self._history) + _, v = zip(*self._history) return sum(v[-nb:]) / float(v[-1] - v[-nb]) From cd77205cf921845d5615805562a28924e8724199 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 13:00:15 +0100 Subject: [PATCH 07/20] Remove superfluous parenthesis --- glances/plugins/glances_sensors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/plugins/glances_sensors.py b/glances/plugins/glances_sensors.py index 2b797527..cffb822d 100644 --- a/glances/plugins/glances_sensors.py +++ b/glances/plugins/glances_sensors.py @@ -182,7 +182,7 @@ class Plugin(GlancesPlugin): for i in self.stats: # Do not display anything if no battery are detected - if (i['type'] == 'battery' and i['value'] == []): + if i['type'] == 'battery' and i['value'] == []: continue # New line ret.append(self.curse_new_line()) From 7f902d3f5b8279f247a70b78d40fb1110d6cae7c Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 13:00:54 +0100 Subject: [PATCH 08/20] Remove duplicate key 'c' in dictionary --- glances/outputs/glances_curses.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index 539285e7..38c83196 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -87,8 +87,7 @@ class _GlancesCurses(object): 'm': {'auto_sort': False, 'sort_key': 'memory_percent'}, 'p': {'auto_sort': False, 'sort_key': 'name'}, 't': {'auto_sort': False, 'sort_key': 'cpu_times'}, - 'u': {'auto_sort': False, 'sort_key': 'username'}, - 'c': {'auto_sort': False, 'sort_key': 'cpu_percent'} + 'u': {'auto_sort': False, 'sort_key': 'username'} } def __init__(self, config=None, args=None): From 34434c5f44ed19e6a8e70996782f9839d6659e1d Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 13:31:17 +0100 Subject: [PATCH 09/20] Fix some bare except statements --- glances/exports/glances_cassandra.py | 2 +- glances/folder_list.py | 2 +- glances/plugins/glances_docker.py | 2 +- glances/plugins/glances_network.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/glances/exports/glances_cassandra.py b/glances/exports/glances_cassandra.py index 0ac83386..db806698 100644 --- a/glances/exports/glances_cassandra.py +++ b/glances/exports/glances_cassandra.py @@ -118,7 +118,7 @@ class Export(GlancesExport): # Table try: session.execute("CREATE TABLE %s (plugin text, time timeuuid, stat map, PRIMARY KEY (plugin, time)) WITH CLUSTERING ORDER BY (time DESC)" % self.table) - except: + except Exception: logger.debug("Cassandra table %s already exist" % self.table) return cluster, session diff --git a/glances/folder_list.py b/glances/folder_list.py index 8b3fbf47..01aa029e 100644 --- a/glances/folder_list.py +++ b/glances/folder_list.py @@ -93,7 +93,7 @@ class FolderList(object): for i in ['careful', 'warning', 'critical']: try: value[i] = self.config.get_value(section, key + i) - except: + except Exception: value[i] = None logger.debug("No {} threshold for folder {}".format(i, value["path"])) diff --git a/glances/plugins/glances_docker.py b/glances/plugins/glances_docker.py index 3af4d557..8c12ac12 100644 --- a/glances/plugins/glances_docker.py +++ b/glances/plugins/glances_docker.py @@ -158,7 +158,7 @@ class Plugin(GlancesPlugin): # First time, try to connect to the server try: self.docker_client = self.connect() - except: + except Exception: docker_tag = False else: if self.docker_client is None: diff --git a/glances/plugins/glances_network.py b/glances/plugins/glances_network.py index 38792ad0..370a6065 100644 --- a/glances/plugins/glances_network.py +++ b/glances/plugins/glances_network.py @@ -98,7 +98,7 @@ class Plugin(GlancesPlugin): netstatus = {} try: netstatus = psutil.net_if_stats() - except: + except AttributeError: pass # Previous network interface stats are stored in the network_old variable From 3d9dce7ec2ff96fd1f586a5fd6d965c8bab83e07 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 20 Dec 2016 13:35:53 +0100 Subject: [PATCH 10/20] Fix another bare except statement (forgot in the previous commit) --- glances/plugins/glances_quicklook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/plugins/glances_quicklook.py b/glances/plugins/glances_quicklook.py index 9e14b541..9a3f3284 100644 --- a/glances/plugins/glances_quicklook.py +++ b/glances/plugins/glances_quicklook.py @@ -28,7 +28,7 @@ import psutil cpuinfo_tag = False try: from cpuinfo import cpuinfo -except: +except ImportError: # Correct issue #754 # Waiting for a correction on the upstream Cpuinfo lib pass From c93ee098d19c5f5cc1431c0fd3a85786c434da5d Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 11:16:29 +0100 Subject: [PATCH 11/20] Fix redefinition of ret type from str to tuple --- glances/password_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/password_list.py b/glances/password_list.py index dbfc7ddd..94c6b4ad 100644 --- a/glances/password_list.py +++ b/glances/password_list.py @@ -38,7 +38,7 @@ class GlancesPasswordList(GlancesPassword): def load(self, config): """Load the password from the configuration file.""" - password_dict = [] + password_dict = {} if config is None: logger.warning("No configuration file available. Cannot load password list.") From a08f848b1adb3324c0ae0e31812031ea6d2539fb Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 11:21:21 +0100 Subject: [PATCH 12/20] Fix redefinition of t type from threading.Thread to glances.timer.Timer --- glances/plugins/glances_ip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glances/plugins/glances_ip.py b/glances/plugins/glances_ip.py index 36cd11c4..565dfc1d 100644 --- a/glances/plugins/glances_ip.py +++ b/glances/plugins/glances_ip.py @@ -173,9 +173,9 @@ class PublicIpAddress(object): t.daemon = True t.start() - t = Timer(self.timeout) + timer = Timer(self.timeout) ip = None - while not t.finished() and ip is None: + while not timer.finished() and ip is None: if q.qsize() > 0: ip = q.get() From 7310b326058563842a66edc0b7722e0fd8da4623 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 13:14:45 +0100 Subject: [PATCH 13/20] Handle nvmlDeviceGetUtilizationRates(handle).gpu error in the UI too --- glances/plugins/glances_gpu.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/glances/plugins/glances_gpu.py b/glances/plugins/glances_gpu.py index b788020a..7bd944b1 100644 --- a/glances/plugins/glances_gpu.py +++ b/glances/plugins/glances_gpu.py @@ -141,7 +141,10 @@ class Plugin(GlancesPlugin): # GPU CPU msg = '{:8}'.format('proc:') ret.append(self.curse_add_line(msg)) - msg = '{:>7d}%'.format(int(gpu_stats['proc'])) + if gpu_stats['proc'] is None: + msg = '{:>8}'.format('N/A') + else: + msg = '{:>7d}%'.format(int(gpu_stats['proc'])) ret.append(self.curse_add_line( msg, self.get_views(item=gpu_stats[self.get_key()], key='proc', From f02a266ca963cadb7e1b8f8d139daba52771a432 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 13:29:49 +0100 Subject: [PATCH 14/20] Always use nvmlDeviceGetMemoryInfo() to get NVIDIA GPU memory usage nvmlDeviceGetUtilizationRates(handle).memory is not always reliable and plus seems not supported by all NVIDIA cards. --- glances/plugins/glances_gpu.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/glances/plugins/glances_gpu.py b/glances/plugins/glances_gpu.py index 7bd944b1..f8a26fea 100644 --- a/glances/plugins/glances_gpu.py +++ b/glances/plugins/glances_gpu.py @@ -183,7 +183,7 @@ class Plugin(GlancesPlugin): """ Returns a list of NVML device handles, one per device. Can throw NVMLError. """ - return [pynvml.nvmlDeviceGetHandleByIndex(i) for i in range(0, pynvml.nvmlDeviceGetCount())] + return [pynvml.nvmlDeviceGetHandleByIndex(i) for i in range(pynvml.nvmlDeviceGetCount())] def get_device_stats(self): """Get GPU stats""" @@ -215,13 +215,10 @@ class Plugin(GlancesPlugin): def get_mem(self, device_handle): """Get GPU device memory consumption in percent""" try: - return pynvml.nvmlDeviceGetUtilizationRates(device_handle).memory + memory_info = pynvml.nvmlDeviceGetMemoryInfo(device_handle) + return memory_info.used * 100 / memory_info.total except pynvml.NVMLError: - try: - memory_info = pynvml.nvmlDeviceGetMemoryInfo(device_handle) - return memory_info.used * 100 / memory_info.total - except pynvml.NVMLError: - return None + return None def get_proc(self, device_handle): """Get GPU device CPU consumption in percent""" From a9ebe31cdf1f839587403e2a53ede12a3ec6b0a6 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 17:17:53 +0100 Subject: [PATCH 15/20] logging: fix redefinition of ret type from str to tuple While I'm here, fix 'Imports from package logging are not grouped'. --- glances/logger.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/glances/logger.py b/glances/logger.py index 3063382d..91ccb6e5 100644 --- a/glances/logger.py +++ b/glances/logger.py @@ -19,11 +19,14 @@ """Custom logger class.""" -import logging import os import tempfile import json -from logging.config import dictConfig + +import logging +import logging.config + +LOG_FILENAME = os.path.join(tempfile.gettempdir(), 'glances.log') # Define the logging configuration LOGGING_CFG = { @@ -48,8 +51,7 @@ LOGGING_CFG = { "file": { "level": "DEBUG", "class": "logging.handlers.RotatingFileHandler", - "formatter": "standard", - 'filename': os.path.join(tempfile.gettempdir(), 'glances.log') + "formatter": "standard" }, "console": { "level": "CRITICAL", @@ -88,14 +90,13 @@ LOGGING_CFG = { def tempfile_name(): """Return the tempfile name (full path).""" - ret = os.path.join(tempfile.gettempdir(), 'glances.log') - if os.access(ret, os.F_OK) and not os.access(ret, os.W_OK): - print("WARNING: Couldn't write to log file {} (Permission denied)".format(ret)) - ret = tempfile.mkstemp(prefix='glances', suffix='.log', text=True) - print("Create a new log file: {}".format(ret[1])) + if os.access(LOG_FILENAME, os.F_OK) and not os.access(LOG_FILENAME, os.W_OK): + print("WARNING: Couldn't write to '{}' (Permission denied)".format(LOG_FILENAME)) + ret = tempfile.mkstemp(prefix='glances-', suffix='.log', text=True) + print("Create log file at '{}'".format(ret[1])) return ret[1] - return ret + return LOG_FILENAME def glances_logger(env_key='LOG_CFG'): @@ -111,7 +112,7 @@ def glances_logger(env_key='LOG_CFG'): # Overwrite the default logger file LOGGING_CFG['handlers']['file']['filename'] = tempfile_name() - # By default, use the LOGGING_CFG lgger configuration + # By default, use the LOGGING_CFG logger configuration config = LOGGING_CFG # Check if a specific configuration is available @@ -122,8 +123,9 @@ def glances_logger(env_key='LOG_CFG'): config = json.load(f) # Load the configuration - dictConfig(config) + logging.config.dictConfig(config) return _logger + logger = glances_logger() From 904e0f1f32c768970b68a467235adc8aea668b1b Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Wed, 21 Dec 2016 17:53:36 +0100 Subject: [PATCH 16/20] actions: change log message from warning to debug --- glances/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/actions.py b/glances/actions.py index 4364a22e..3ad1dd2b 100644 --- a/glances/actions.py +++ b/glances/actions.py @@ -27,7 +27,7 @@ from glances.timer import Timer try: import pystache except ImportError: - logger.warning("PyStache lib not installed (action script with mustache will not work)") + logger.debug("Pystache library not found (action scripts won't work)") pystache_tag = False else: pystache_tag = True From e8ccb6443746715fe25d1b90b83ed92b6e980d0a Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Thu, 22 Dec 2016 12:00:44 +0100 Subject: [PATCH 17/20] Some minor fixes and misspelling --- conf/glances.conf | 10 +++++----- glances/outputs/glances_curses.py | 7 +++---- glances/plugins/glances_wifi.py | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/conf/glances.conf b/conf/glances.conf index 03e5baa6..89b0d28b 100644 --- a/conf/glances.conf +++ b/conf/glances.conf @@ -3,7 +3,7 @@ ############################################################################## [global] -# Does Glances should check if a newer version is available on Pypi ? +# Does Glances should check if a newer version is available on PyPI ? check_update=true # History size (maximum number of values) # Default is 28800: 1 day with 1 point every 3 seconds (default refresh time) @@ -48,12 +48,12 @@ steal_warning=70 steal_critical=90 #steal_log=True # I/O wait percentage should be lower than 1/# (of CPU cores) -# Let commented for default config (1/#-20% / 1/#-10% / 1/#) +# Leave commented to just use the default config (1/#-20% / 1/#-10% / 1/#) #iowait_careful=30 #iowait_warning=40 #iowait_critical=50 # Context switch limit (core / second) -# Let commented for default config (critical is 56000/# (of CPU core)) +# Leave commented to just use the default config (critical is 56000/# (of CPU core)) #ctx_switches_careful=10000 #ctx_switches_warning=12000 #ctx_switches_critical=14000 @@ -211,7 +211,7 @@ mem_critical=90 # Ports scanner plugin configuration # Interval in second between two scans refresh=30 -# Set the default timeout (in second) for a scan (can be overwrite in the scan list) +# Set the default timeout (in second) for a scan (can be overwritten in the scan list) timeout=3 # If port_default_gateway is True, add the default gateway on top of the scan list port_default_gateway=True @@ -365,7 +365,7 @@ prefix=G # * regex: Regular expression to filter the process(es) # * refresh: The AMP is executed every refresh seconds # * one_line: (optional) Force (if true) the AMP to be displayed in one line -* * command: (optional) command to execute when the process is detected (thk to the regex) +# * command: (optional) command to execute when the process is detected (thk to the regex) # * countmin: (optional) minimal number of processes # A warning will be displayed if number of process < count # * countmax: (optional) maximum number of processes diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index 38c83196..56d0dfd7 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -775,12 +775,11 @@ class _GlancesCurses(object): # Only in standalone mode (cs_status is None) if self.edit_filter and cs_status is None: new_filter = self.display_popup( - 'Process filter pattern: \n' + - '\n' + + 'Process filter pattern: \n\n' + 'Examples:\n' + '- python\n' + '- .*python.*\n' + - '- \/usr\/lib.*' + + '- \/usr\/lib.*\n' + '- name:.*nautilus.*\n' + '- cmdline:.*glances.*\n' + '- username:nicolargo\n' + @@ -788,7 +787,7 @@ class _GlancesCurses(object): is_input=True, input_value=glances_processes.process_filter_input) glances_processes.process_filter = new_filter - elif self.edit_filter and cs_status != 'None': + elif self.edit_filter and cs_status is not None: self.display_popup('Process filter only available in standalone mode') self.edit_filter = False diff --git a/glances/plugins/glances_wifi.py b/glances/plugins/glances_wifi.py index fd5921d5..6da04217 100644 --- a/glances/plugins/glances_wifi.py +++ b/glances/plugins/glances_wifi.py @@ -30,7 +30,7 @@ import psutil try: from wifi.scan import Cell from wifi.exceptions import InterfaceError -except ImportError as e: +except ImportError: logger.debug("Wifi library not found. Glances cannot grab Wifi info.") wifi_tag = False else: From 6fb03571f106b59a168bd895812b42ffbd47548e Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Thu, 22 Dec 2016 15:44:18 +0100 Subject: [PATCH 18/20] Use triple double-quote string format for docstrings --- glances/outputs/glances_curses.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index 56d0dfd7..2030b531 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -36,8 +36,7 @@ if not WINDOWS: import curses.panel from curses.textpad import Textbox except ImportError: - logger.critical( - "Curses module not found. Glances cannot start in standalone mode.") + logger.critical("Curses module not found. Glances cannot start in standalone mode.") sys.exit(1) else: from glances.outputs.glances_colorconsole import WCurseLight @@ -140,7 +139,7 @@ class _GlancesCurses(object): self._init_history() def load_config(self, config): - '''Load the outputs section of the configuration file''' + """Load the outputs section of the configuration file.""" # Load the theme if config is not None and config.has_section('outputs'): logger.debug('Read the outputs section in the configuration file') @@ -148,11 +147,11 @@ class _GlancesCurses(object): logger.debug('Theme for the curse interface: {}'.format(self.theme['name'])) def is_theme(self, name): - '''Return True if the theme *name* should be used''' + """Return True if the theme *name* should be used.""" return getattr(self.args, 'theme_' + name) or self.theme['name'] == name def _init_history(self): - '''Init the history option''' + """Init the history option.""" self.reset_history_tag = False self.graph_tag = False @@ -166,7 +165,7 @@ class _GlancesCurses(object): logger.error('Export graphs disabled') def _init_cursor(self): - '''Init cursors''' + """Init cursors.""" if hasattr(curses, 'noecho'): curses.noecho() @@ -175,7 +174,7 @@ class _GlancesCurses(object): self.set_cursor(0) def _init_colors(self): - '''Init the Curses color layout''' + """Init the Curses color layout.""" # Set curses options if hasattr(curses, 'start_color'): From 9f36d12693475031e9875256d6a9810dcd952889 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Thu, 22 Dec 2016 15:58:01 +0100 Subject: [PATCH 19/20] Replace assignment with augmented assignment --- glances/plugins/glances_wifi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/plugins/glances_wifi.py b/glances/plugins/glances_wifi.py index 6da04217..4b638f20 100644 --- a/glances/plugins/glances_wifi.py +++ b/glances/plugins/glances_wifi.py @@ -188,7 +188,7 @@ class Plugin(GlancesPlugin): hotspotname = i['ssid'] # Add the encryption type (if it is available) if i['encrypted']: - hotspotname = hotspotname + ' {}'.format(i['encryption_type']) + hotspotname += ' {}'.format(i['encryption_type']) # Cut hotspotname if it is too long if len(hotspotname) > ifname_max_width: hotspotname = '_' + hotspotname[-ifname_max_width + 1:] From d9aaa0147d13535ee137694765f61cc1d68371aa Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Thu, 22 Dec 2016 16:00:00 +0100 Subject: [PATCH 20/20] Remove unreachable code --- glances/password_list.py | 1 - 1 file changed, 1 deletion(-) diff --git a/glances/password_list.py b/glances/password_list.py index 94c6b4ad..70e6e2b9 100644 --- a/glances/password_list.py +++ b/glances/password_list.py @@ -70,7 +70,6 @@ class GlancesPasswordList(GlancesPassword): return self._password_dict['default'] except (KeyError, TypeError): return None - return None def set_password(self, host, password): """Set a password for a specific host."""