Completed
Push — master ( 2b80fa...6ea077 )
by Nicolas
01:22
created

Config.sections()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2017 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Manage the configuration file."""
21
22
import os
23
import sys
24
import multiprocessing
25
from io import open
26
27
from glances.compat import ConfigParser, NoOptionError
28
from glances.globals import BSD, LINUX, MACOS, WINDOWS, sys_prefix
29
from glances.logger import logger
30
31
32
class Config(object):
33
34
    """This class is used to access/read config file, if it exists.
35
36
    :param config_dir: the path to search for config file
37
    :type config_dir: str or None
38
    """
39
40
    def __init__(self, config_dir=None):
41
        self.config_dir = config_dir
42
        self.config_filename = 'glances.conf'
43
        self._loaded_config_file = None
44
45
        self.parser = ConfigParser()
46
        self.read()
47
48
    def config_file_paths(self):
49
        r"""Get a list of config file paths.
50
51
        The list is built taking into account of the OS, priority and location.
52
53
        * custom path: /path/to/glances
54
        * Linux: ~/.config/glances, /etc/glances
55
        * *BSD: ~/.config/glances, /usr/local/etc/glances
56
        * macOS: ~/Library/Application Support/glances, /usr/local/etc/glances
57
        * Windows: %APPDATA%\glances
58
59
        The config file will be searched in the following order of priority:
60
            * /path/to/file (via -C flag)
61
            * user's home directory (per-user settings)
62
            * system-wide directory (system-wide settings)
63
        """
64
        paths = []
65
66
        if self.config_dir:
67
            paths.append(self.config_dir)
68
69
        if LINUX or BSD:
70
            paths.append(
71
                os.path.join(os.environ.get('XDG_CONFIG_HOME') or
72
                             os.path.expanduser('~/.config'),
73
                             'glances', self.config_filename))
74
            if BSD:
75
                paths.append(os.path.join(sys.prefix, 'etc', 'glances', self.config_filename))
76
            else:
77
                paths.append(os.path.join('/etc/glances', self.config_filename))
78
        elif MACOS:
79
            paths.append(
80
                os.path.join(os.path.expanduser('~/Library/Application Support/glances'),
81
                             self.config_filename))
82
            paths.append(os.path.join(sys_prefix, 'etc', 'glances', self.config_filename))
83
        elif WINDOWS:
84
            paths.append(os.path.join(os.environ.get('APPDATA'), 'glances', self.config_filename))
85
86
        return paths
87
88
    def read(self):
89
        """Read the config file, if it exists. Using defaults otherwise."""
90
        for config_file in self.config_file_paths():
91
            if os.path.exists(config_file):
92
                try:
93
                    with open(config_file, encoding='utf-8') as f:
94
                        self.parser.read_file(f)
95
                        self.parser.read(f)
96
                    logger.info("Read configuration file '{}'".format(config_file))
97
                except UnicodeDecodeError as err:
98
                    logger.error("Cannot decode configuration file '{}': {}".format(config_file, err))
99
                    sys.exit(1)
100
                # Save the loaded configuration file path (issue #374)
101
                self._loaded_config_file = config_file
102
                break
103
104
        # Quicklook
105
        if not self.parser.has_section('quicklook'):
106
            self.parser.add_section('quicklook')
107
        self.set_default_cwc('quicklook', 'cpu')
108
        self.set_default_cwc('quicklook', 'mem')
109
        self.set_default_cwc('quicklook', 'swap')
110
111
        # CPU
112
        if not self.parser.has_section('cpu'):
113
            self.parser.add_section('cpu')
114
        self.set_default_cwc('cpu', 'user')
115
        self.set_default_cwc('cpu', 'system')
116
        self.set_default_cwc('cpu', 'steal')
117
        # By default I/O wait should be lower than 1/number of CPU cores
118
        iowait_bottleneck = (1.0 / multiprocessing.cpu_count()) * 100.0
119
        self.set_default_cwc('cpu', 'iowait',
120
                             [str(iowait_bottleneck - (iowait_bottleneck * 0.20)),
121
                              str(iowait_bottleneck - (iowait_bottleneck * 0.10)),
122
                              str(iowait_bottleneck)])
123
        ctx_switches_bottleneck = 56000 / multiprocessing.cpu_count()
124
        self.set_default_cwc('cpu', 'ctx_switches',
125
                             [str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.20)),
126
                              str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.10)),
127
                              str(ctx_switches_bottleneck)])
128
129
        # Per-CPU
130
        if not self.parser.has_section('percpu'):
131
            self.parser.add_section('percpu')
132
        self.set_default_cwc('percpu', 'user')
133
        self.set_default_cwc('percpu', 'system')
134
135
        # Load
136
        if not self.parser.has_section('load'):
137
            self.parser.add_section('load')
138
        self.set_default_cwc('load', cwc=['0.7', '1.0', '5.0'])
139
140
        # Mem
141
        if not self.parser.has_section('mem'):
142
            self.parser.add_section('mem')
143
        self.set_default_cwc('mem')
144
145
        # Swap
146
        if not self.parser.has_section('memswap'):
147
            self.parser.add_section('memswap')
148
        self.set_default_cwc('memswap')
149
150
        # NETWORK
151
        if not self.parser.has_section('network'):
152
            self.parser.add_section('network')
153
        self.set_default_cwc('network', 'rx')
154
        self.set_default_cwc('network', 'tx')
155
156
        # FS
157
        if not self.parser.has_section('fs'):
158
            self.parser.add_section('fs')
159
        self.set_default_cwc('fs')
160
161
        # Sensors
162
        if not self.parser.has_section('sensors'):
163
            self.parser.add_section('sensors')
164
        self.set_default_cwc('sensors', 'temperature_core', cwc=['60', '70', '80'])
165
        self.set_default_cwc('sensors', 'temperature_hdd', cwc=['45', '52', '60'])
166
        self.set_default_cwc('sensors', 'battery', cwc=['80', '90', '95'])
167
168
        # Process list
169
        if not self.parser.has_section('processlist'):
170
            self.parser.add_section('processlist')
171
        self.set_default_cwc('processlist', 'cpu')
172
        self.set_default_cwc('processlist', 'mem')
173
174
    @property
175
    def loaded_config_file(self):
176
        """Return the loaded configuration file."""
177
        return self._loaded_config_file
178
179
    def as_dict(self):
180
        """Return the configuration as a dict"""
181
        dictionary = {}
182
        for section in self.parser.sections():
183
            dictionary[section] = {}
184
            for option in self.parser.options(section):
185
                dictionary[section][option] = self.parser.get(section, option)
186
        return dictionary
187
188
    def sections(self):
189
        """Return a list of all sections."""
190
        return self.parser.sections()
191
192
    def items(self, section):
193
        """Return the items list of a section."""
194
        return self.parser.items(section)
195
196
    def has_section(self, section):
197
        """Return info about the existence of a section."""
198
        return self.parser.has_section(section)
199
200
    def set_default_cwc(self, section,
201
                        option_header=None,
202
                        cwc=['50', '70', '90']):
203
        """Set default values for careful, warning and critical."""
204
        if option_header is None:
205
            header = ''
206
        else:
207
            header = option_header + '_'
208
        self.set_default(section, header + 'careful', cwc[0])
209
        self.set_default(section, header + 'warning', cwc[1])
210
        self.set_default(section, header + 'critical', cwc[2])
211
212
    def set_default(self, section, option, default):
213
        """If the option did not exist, create a default value."""
214
        if not self.parser.has_option(section, option):
215
            self.parser.set(section, option, default)
216
217
    def get_value(self, section, option, default=None):
218
        """Get the value of an option, if it exists."""
219
        try:
220
            return self.parser.get(section, option)
221
        except NoOptionError:
222
            return default
223
224
    def get_int_value(self, section, option, default=0):
225
        """Get the int value of an option, if it exists."""
226
        try:
227
            return self.parser.getint(section, option)
228
        except NoOptionError:
229
            return int(default)
230
231
    def get_float_value(self, section, option, default=0.0):
232
        """Get the float value of an option, if it exists."""
233
        try:
234
            return self.parser.getfloat(section, option)
235
        except NoOptionError:
236
            return float(default)
237