Passed
Push — master ( cdb6bf...0d35d5 )
by Nicolas
04:08 queued 14s
created

glances.plugins.glances_docker.Plugin.update()   F

Complexity

Conditions 14

Size

Total Lines 126
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 72
nop 1
dl 0
loc 126
rs 3.4363
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

Complexity

Complex classes like glances.plugins.glances_docker.Plugin.update() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 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
"""Docker plugin."""
21
22
import os
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
23
import threading
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
24
import time
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
25
26
from glances.logger import logger
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
27
from glances.compat import iterkeys, itervalues, nativestr
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
28
from glances.timer import getTimeSinceLastUpdate
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
29
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
30
from glances.processes import sort_stats as sort_stats_processes, weighted, glances_processes
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (93/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
import missing from __future__ import absolute_import
Loading history...
Unused Code introduced by
Unused weighted imported from glances.processes
Loading history...
31
32
# Docker-py library (optional and Linux-only)
33
# https://github.com/docker/docker-py
34
try:
35
    import docker
0 ignored issues
show
introduced by
import missing from __future__ import absolute_import
Loading history...
introduced by
Unable to import 'docker'
Loading history...
36
except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
37
    import_error_tag = True
0 ignored issues
show
Coding Style Naming introduced by
The name import_error_tag does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
38
    # Display debu message if import KeyError
39
    logger.warning("Error loading Docker Python Lib. Docker plugin is disabled ({})".format(e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (95/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
40
else:
41
    import_error_tag = False
0 ignored issues
show
Coding Style Naming introduced by
The name import_error_tag does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
42
43
# Define the items history list (list of items to add to history)
44
# TODO: For the moment limited to the CPU. Had to change the graph exports
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
45
#       method to display one graph per container.
46
# items_history_list = [{'name': 'cpu_percent',
47
#                        'description': 'Container CPU consumption in %',
48
#                        'y_unit': '%'},
49
#                       {'name': 'memory_usage',
50
#                        'description': 'Container memory usage in bytes',
51
#                        'y_unit': 'B'},
52
#                       {'name': 'network_rx',
53
#                        'description': 'Container network RX bitrate in bits per second',
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
54
#                        'y_unit': 'bps'},
55
#                       {'name': 'network_tx',
56
#                        'description': 'Container network TX bitrate in bits per second',
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
57
#                        'y_unit': 'bps'},
58
#                       {'name': 'io_r',
59
#                        'description': 'Container IO bytes read per second',
60
#                        'y_unit': 'Bps'},
61
#                       {'name': 'io_w',
62
#                        'description': 'Container IO bytes write per second',
63
#                        'y_unit': 'Bps'}]
64
items_history_list = [{'name': 'cpu_percent',
0 ignored issues
show
Coding Style Naming introduced by
The name items_history_list does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
65
                       'description': 'Container CPU consumption in %',
66
                       'y_unit': '%'}]
67
68
69
class Plugin(GlancesPlugin):
0 ignored issues
show
best-practice introduced by
Too many instance attributes (9/7)
Loading history...
70
    """Glances Docker plugin.
71
72
    stats is a dict: {'version': {...}, 'containers': [{}, {}]}
73
    """
74
75
    def __init__(self, args=None, config=None):
76
        """Init the plugin."""
77
        super(Plugin, self).__init__(args=args,
78
                                     config=config,
79
                                     items_history_list=items_history_list)
80
81
        # The plgin can be disable using: args.disable_docker
82
        self.args = args
83
84
        # Default config keys
85
        self.config = config
86
87
        # We want to display the stat in the curse interface
88
        self.display_curse = True
89
90
        # Init the Docker API
91
        self.docker_client = self.connect()
92
93
        # Dict of thread (to grab stats asynchroniously, one thread is created by container)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (92/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
94
        # key: Container Id
95
        # value: instance of ThreadDockerGrabber
96
        self.thread_list = {}
97
98
    def exit(self):
99
        """Overwrite the exit method to close threads."""
100
        for t in itervalues(self.thread_list):
0 ignored issues
show
Coding Style Naming introduced by
The name t does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
101
            t.stop()
102
        # Call the father class
103
        super(Plugin, self).exit()
104
105
    def get_key(self):
106
        """Return the key of the list."""
107
        return 'name'
108
109
    def get_export(self):
110
        """Overwrite the default export method.
111
112
        - Only exports containers
113
        - The key is the first container name
114
        """
115
        ret = []
116
        try:
117
            ret = self.stats['containers']
118
        except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
119
            logger.debug("docker plugin - Docker export error {}".format(e))
0 ignored issues
show
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
120
        return ret
121
122
    def connect(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
123
        """Connect to the Docker server."""
124
        try:
125
            ret = docker.from_env()
126
        except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
127
            logger.error("docker plugin - Can not connect to Docker ({})".format(e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
128
            ret = None
129
130
        return ret
131
132
    def _all_tag(self):
133
        """Return the all tag of the Glances/Docker configuration file.
134
135
        # By default, Glances only display running containers
136
        # Set the following key to True to display all containers
137
        all=True
138
        """
139
        all_tag = self.get_conf_value('all')
140
        if len(all_tag) == 0:
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
Unused Code introduced by
Do not use len(SEQUENCE) as condition value
Loading history...
141
            return False
142
        else:
143
            return all_tag[0].lower() == 'true'
144
145
    @GlancesPlugin._check_decorator
146
    @GlancesPlugin._log_result_decorator
147
    def update(self):
148
        """Update Docker stats using the input method."""
149
        # Init new stats
150
        stats = self.get_init_value()
151
152
        # The Docker-py lib is mandatory
153
        if import_error_tag:
154
            return self.stats
155
156
        if self.input_method == 'local':
157
            # Update stats
158
159
            # Docker version
160
            # Exemple: {
161
            #     "KernelVersion": "3.16.4-tinycore64",
162
            #     "Arch": "amd64",
163
            #     "ApiVersion": "1.15",
164
            #     "Version": "1.3.0",
165
            #     "GitCommit": "c78088f",
166
            #     "Os": "linux",
167
            #     "GoVersion": "go1.3.3"
168
            # }
169
            try:
170
                stats['version'] = self.docker_client.version()
171
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
172
                # Correct issue#649
173
                logger.error("{} plugin - Cannot get Docker version ({})".format(self.plugin_name, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
174
                # We may have lost connection remove version info
175
                if 'version' in self.stats:
176
                    del self.stats['version']
177
                self.stats['containers'] = []
178
                return self.stats
179
180
            # Update current containers list
181
            try:
182
                # Issue #1152: Docker module doesn't export details about stopped containers
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (92/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
183
                # The Docker/all key of the configuration file should be set to True
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
184
                containers = self.docker_client.containers.list(all=self._all_tag()) or []
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (90/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
185
            except Exception as e:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
186
                logger.error("{} plugin - Cannot get containers list ({})".format(self.plugin_name, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
187
                # We may have lost connection empty the containers list.
188
                self.stats['containers'] = []
189
                return self.stats
190
191
            # Start new thread for new container
192
            for container in containers:
193
                if container.id not in self.thread_list:
194
                    # Thread did not exist in the internal dict
195
                    # Create it and add it to the internal dict
196
                    logger.debug("{} plugin - Create thread for container {}".format(self.plugin_name, container.id[:12]))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (122/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
197
                    t = ThreadDockerGrabber(container)
0 ignored issues
show
Coding Style Naming introduced by
The name t does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
198
                    self.thread_list[container.id] = t
199
                    t.start()
200
201
            # Stop threads for non-existing containers
202
            nonexisting_containers = set(iterkeys(self.thread_list)) - set([c.id for c in containers])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
203
            for container_id in nonexisting_containers:
204
                # Stop the thread
205
                logger.debug("{} plugin - Stop thread for old container {}".format(self.plugin_name, container_id[:12]))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
206
                self.thread_list[container_id].stop()
207
                # Delete the item from the dict
208
                del self.thread_list[container_id]
209
210
            # Get stats for all containers
211
            stats['containers'] = []
212
            for container in containers:
213
                # Only show specific containers
214
                if not self.is_show(nativestr(container.name)):
215
                    continue
216
217
                # Do not take hiden container into account
218
                if self.is_hide(nativestr(container.name)):
219
                    continue
220
221
                # Init the stats for the current container
222
                container_stats = {}
223
                # The key is the container name and not the Id
224
                container_stats['key'] = self.get_key()
225
                # Export name (first name in the Names list, without the /)
226
                container_stats['name'] = nativestr(container.name)
227
                # Export global Names (used by the WebUI)
228
                container_stats['Names'] = [nativestr(container.name)]
229
                # Container Id
230
                container_stats['Id'] = container.id
231
                # Container Image
232
                container_stats['Image'] = container.image.tags
233
                # Global stats (from attrs)
234
                container_stats['Status'] = container.attrs['State']['Status']
235
                container_stats['Command'] = container.attrs['Config']['Entrypoint']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
236
                # Standards stats
237
                if container_stats['Status'] in ('running', 'paused'):
238
                    container_stats['cpu'] = self.get_docker_cpu(container.id, self.thread_list[container.id].stats)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
239
                    container_stats['cpu_percent'] = container_stats['cpu'].get('total', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
240
                    container_stats['memory'] = self.get_docker_memory(container.id, self.thread_list[container.id].stats)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (122/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
241
                    container_stats['memory_usage'] = container_stats['memory'].get('usage', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (98/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
242
                    container_stats['io'] = self.get_docker_io(container.id, self.thread_list[container.id].stats)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
243
                    container_stats['io_r'] = container_stats['io'].get('ior', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
244
                    container_stats['io_w'] = container_stats['io'].get('iow', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
245
                    container_stats['network'] = self.get_docker_network(container.id, self.thread_list[container.id].stats)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
246
                    container_stats['network_rx'] = container_stats['network'].get('rx', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
247
                    container_stats['network_tx'] = container_stats['network'].get('tx', None)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
248
                else:
249
                    container_stats['cpu'] = {}
250
                    container_stats['cpu_percent'] = None
251
                    container_stats['memory'] = {}
252
                    container_stats['memory_percent'] = None
253
                    container_stats['io'] = {}
254
                    container_stats['io_r'] = None
255
                    container_stats['io_w'] = None
256
                    container_stats['network'] = {}
257
                    container_stats['network_rx'] = None
258
                    container_stats['network_tx'] = None
259
                # Add current container stats to the stats list
260
                stats['containers'].append(container_stats)
261
262
        elif self.input_method == 'snmp':
263
            # Update stats using SNMP
264
            # Not available
265
            pass
266
267
        # Sort and update the stats
268
        self.stats = sort_stats(stats)
269
270
        return self.stats
271
272
    def get_docker_cpu(self, container_id, all_stats):
273
        """Return the container CPU usage.
274
275
        Input: id is the full container id
276
               all_stats is the output of the stats method of the Docker API
277
        Output: a dict {'total': 1.49}
278
        """
279
        cpu_new = {}
280
        ret = {'total': 0.0}
281
282
        # Read the stats
283
        # For each container, you will find a pseudo-file cpuacct.stat,
284
        # containing the CPU usage accumulated by the processes of the container.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
285
        # Those times are expressed in ticks of 1/USER_HZ of a second.
286
        # On x86 systems, USER_HZ is 100.
287
        try:
288
            cpu_new['total'] = all_stats['cpu_stats']['cpu_usage']['total_usage']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
289
            cpu_new['system'] = all_stats['cpu_stats']['system_cpu_usage']
290
            cpu_new['nb_core'] = len(all_stats['cpu_stats']['cpu_usage']['percpu_usage'] or [])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (95/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
291
        except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
292
            # all_stats do not have CPU information
293
            logger.debug("docker plugin - Cannot grab CPU usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
294
            logger.debug(all_stats)
295
        else:
296
            # Previous CPU stats stored in the cpu_old variable
297
            if not hasattr(self, 'cpu_old'):
298
                # First call, we init the cpu_old variable
299
                self.cpu_old = {}
0 ignored issues
show
Coding Style introduced by
The attribute cpu_old was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
300
                try:
301
                    self.cpu_old[container_id] = cpu_new
302
                except (IOError, UnboundLocalError):
303
                    pass
304
305
            if container_id not in self.cpu_old:
306
                try:
307
                    self.cpu_old[container_id] = cpu_new
308
                except (IOError, UnboundLocalError):
309
                    pass
310
            else:
311
                #
312
                cpu_delta = float(cpu_new['total'] - self.cpu_old[container_id]['total'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (89/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
313
                system_delta = float(cpu_new['system'] - self.cpu_old[container_id]['system'])
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
314
                if cpu_delta > 0.0 and system_delta > 0.0:
315
                    ret['total'] = (cpu_delta / system_delta) * float(cpu_new['nb_core']) * 100
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (95/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
division w/o __future__ statement
Loading history...
316
317
                # Save stats to compute next stats
318
                self.cpu_old[container_id] = cpu_new
319
320
        # Return the stats
321
        return ret
322
323
    def get_docker_memory(self, container_id, all_stats):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
324
        """Return the container MEMORY.
325
326
        Input: id is the full container id
327
               all_stats is the output of the stats method of the Docker API
328
        Output: a dict {'rss': 1015808, 'cache': 356352,  'usage': ..., 'max_usage': ...}
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (89/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
329
        """
330
        ret = {}
331
        # Read the stats
332
        try:
333
            ret['rss'] = all_stats['memory_stats']['stats']['rss']
334
            ret['cache'] = all_stats['memory_stats']['stats']['cache']
335
            ret['usage'] = all_stats['memory_stats']['usage']
336
            ret['limit'] = all_stats['memory_stats']['limit']
337
            ret['max_usage'] = all_stats['memory_stats']['max_usage']
338
        except (KeyError, TypeError) as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
339
            # all_stats do not have MEM information
340
            logger.debug("docker plugin - Cannot grab MEM usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
341
            logger.debug(all_stats)
342
        # Return the stats
343
        return ret
344
345
    def get_docker_network(self, container_id, all_stats):
346
        """Return the container network usage using the Docker API (v1.0 or higher).
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
347
348
        Input: id is the full container id
349
        Output: a dict {'time_since_update': 3000, 'rx': 10, 'tx': 65}.
350
        with:
351
            time_since_update: number of seconds elapsed between the latest grab
352
            rx: Number of byte received
353
            tx: Number of byte transmited
354
        """
355
        # Init the returned dict
356
        network_new = {}
357
358
        # Read the rx/tx stats (in bytes)
359
        try:
360
            netcounters = all_stats["networks"]
361
        except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
362
            # all_stats do not have NETWORK information
363
            logger.debug("docker plugin - Cannot grab NET usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
364
            logger.debug(all_stats)
365
            # No fallback available...
366
            return network_new
367
368
        # Previous network interface stats are stored in the network_old variable
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
369
        if not hasattr(self, 'inetcounters_old'):
370
            # First call, we init the network_old var
371
            self.netcounters_old = {}
0 ignored issues
show
Coding Style introduced by
The attribute netcounters_old was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
372
            try:
373
                self.netcounters_old[container_id] = netcounters
374
            except (IOError, UnboundLocalError):
375
                pass
376
377
        if container_id not in self.netcounters_old:
378
            try:
379
                self.netcounters_old[container_id] = netcounters
380
            except (IOError, UnboundLocalError):
381
                pass
382
        else:
383
            # By storing time data we enable Rx/s and Tx/s calculations in the
384
            # XML/RPC API, which would otherwise be overly difficult work
385
            # for users of the API
386
            try:
387
                network_new['time_since_update'] = getTimeSinceLastUpdate('docker_net_{}'.format(container_id))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
388
                network_new['rx'] = netcounters["eth0"]["rx_bytes"] - self.netcounters_old[container_id]["eth0"]["rx_bytes"]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
389
                network_new['tx'] = netcounters["eth0"]["tx_bytes"] - self.netcounters_old[container_id]["eth0"]["tx_bytes"]
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
390
                network_new['cumulative_rx'] = netcounters["eth0"]["rx_bytes"]
391
                network_new['cumulative_tx'] = netcounters["eth0"]["tx_bytes"]
392
            except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
393
                # all_stats do not have INTERFACE information
394
                logger.debug("docker plugin - Cannot grab network interface usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (129/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
395
                logger.debug(all_stats)
396
397
            # Save stats to compute next bitrate
398
            self.netcounters_old[container_id] = netcounters
399
400
        # Return the stats
401
        return network_new
402
403
    def get_docker_io(self, container_id, all_stats):
404
        """Return the container IO usage using the Docker API (v1.0 or higher).
405
406
        Input: id is the full container id
407
        Output: a dict {'time_since_update': 3000, 'ior': 10, 'iow': 65}.
408
        with:
409
            time_since_update: number of seconds elapsed between the latest grab
410
            ior: Number of byte readed
411
            iow: Number of byte written
412
        """
413
        # Init the returned dict
414
        io_new = {}
415
416
        # Read the ior/iow stats (in bytes)
417
        try:
418
            iocounters = all_stats["blkio_stats"]
419
        except KeyError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
420
            # all_stats do not have io information
421
            logger.debug("docker plugin - Cannot grab block IO usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (116/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
422
            logger.debug(all_stats)
423
            # No fallback available...
424
            return io_new
425
426
        # Previous io interface stats are stored in the io_old variable
427
        if not hasattr(self, 'iocounters_old'):
428
            # First call, we init the io_old var
429
            self.iocounters_old = {}
0 ignored issues
show
Coding Style introduced by
The attribute iocounters_old was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
430
            try:
431
                self.iocounters_old[container_id] = iocounters
432
            except (IOError, UnboundLocalError):
433
                pass
434
435
        if container_id not in self.iocounters_old:
436
            try:
437
                self.iocounters_old[container_id] = iocounters
438
            except (IOError, UnboundLocalError):
439
                pass
440
        else:
441
            # By storing time data we enable IoR/s and IoW/s calculations in the
442
            # XML/RPC API, which would otherwise be overly difficult work
443
            # for users of the API
444
            try:
445
                # Read IOR and IOW value in the structure list of dict
446
                ior = [i for i in iocounters['io_service_bytes_recursive'] if i['op'] == 'Read'][0]['value']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (108/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
447
                iow = [i for i in iocounters['io_service_bytes_recursive'] if i['op'] == 'Write'][0]['value']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (109/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
448
                ior_old = [i for i in self.iocounters_old[container_id]['io_service_bytes_recursive'] if i['op'] == 'Read'][0]['value']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (135/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
449
                iow_old = [i for i in self.iocounters_old[container_id]['io_service_bytes_recursive'] if i['op'] == 'Write'][0]['value']
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (136/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
450
            except (TypeError, IndexError, KeyError) as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
451
                # all_stats do not have io information
452
                logger.debug("docker plugin - Cannot grab block IO usage for container {} ({})".format(container_id, e))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (120/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
453
            else:
454
                io_new['time_since_update'] = getTimeSinceLastUpdate('docker_io_{}'.format(container_id))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
455
                io_new['ior'] = ior - ior_old
456
                io_new['iow'] = iow - iow_old
457
                io_new['cumulative_ior'] = ior
458
                io_new['cumulative_iow'] = iow
459
460
                # Save stats to compute next bitrate
461
                self.iocounters_old[container_id] = iocounters
462
463
        # Return the stats
464
        return io_new
465
466
    def get_user_ticks(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
467
        """Return the user ticks by reading the environment variable."""
468
        return os.sysconf(os.sysconf_names['SC_CLK_TCK'])
469
470
    def get_stats_action(self):
471
        """Return stats for the action.
472
473
        Docker will return self.stats['containers']
474
        """
475
        return self.stats['containers']
476
477
    def update_views(self):
478
        """Update stats views."""
479
        # Call the father's method
480
        super(Plugin, self).update_views()
481
482
        if 'containers' not in self.stats:
483
            return False
484
485
        # Add specifics informations
486
        # Alert
487
        for i in self.stats['containers']:
488
            # Init the views for the current container (key = container name)
489
            self.views[i[self.get_key()]] = {'cpu': {}, 'mem': {}}
490
            # CPU alert
491
            if 'cpu' in i and 'total' in i['cpu']:
492
                # Looking for specific CPU container threasold in the conf file
493
                alert = self.get_alert(i['cpu']['total'],
494
                                       header=i['name'] + '_cpu',
495
                                       action_key=i['name'])
496
                if alert == 'DEFAULT':
497
                    # Not found ? Get back to default CPU threasold value
498
                    alert = self.get_alert(i['cpu']['total'], header='cpu')
499
                self.views[i[self.get_key()]]['cpu']['decoration'] = alert
500
            # MEM alert
501
            if 'memory' in i and 'usage' in i['memory']:
502
                # Looking for specific MEM container threasold in the conf file
503
                alert = self.get_alert(i['memory']['usage'],
504
                                       maximum=i['memory']['limit'],
505
                                       header=i['name'] + '_mem',
506
                                       action_key=i['name'])
507
                if alert == 'DEFAULT':
508
                    # Not found ? Get back to default MEM threasold value
509
                    alert = self.get_alert(i['memory']['usage'],
510
                                           maximum=i['memory']['limit'],
511
                                           header='mem')
512
                self.views[i[self.get_key()]]['mem']['decoration'] = alert
513
514
        return True
515
516
    def msg_curse(self, args=None, max_width=None):
517
        """Return the dict to display in the curse interface."""
518
        # Init the return message
519
        ret = []
520
521
        # Only process if stats exist (and non null) and display plugin enable...
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (81/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
522
        if not self.stats \
523
           or 'containers' not in self.stats or len(self.stats['containers']) == 0 \
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Unused Code introduced by
Do not use len(SEQUENCE) as condition value
Loading history...
524
           or self.is_disable():
525
            return ret
526
527
        # Build the string message
528
        # Title
529
        msg = '{}'.format('CONTAINERS')
530
        ret.append(self.curse_add_line(msg, "TITLE"))
531
        msg = ' {}'.format(len(self.stats['containers']))
532
        ret.append(self.curse_add_line(msg))
533
        msg = ' (served by Docker {})'.format(self.stats['version']["Version"])
534
        ret.append(self.curse_add_line(msg))
535
        ret.append(self.curse_new_line())
536
        # Header
537
        ret.append(self.curse_new_line())
538
        # Get the maximum containers name
539
        # Max size is configurable. See feature request #1723.
540
        name_max_width = min(self.config.get_int_value('docker', 'max_name_size', default=20),
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (94/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
541
                             len(max(self.stats['containers'], 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
542
                                 key=lambda x: len(x['name']))['name']))
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 4 spaces).
Loading history...
543
        msg = ' {:{width}}'.format('Name', width=name_max_width)
544
        ret.append(self.curse_add_line(msg))
545
        msg = '{:>10}'.format('Status')
546
        ret.append(self.curse_add_line(msg))
547
        msg = '{:>6}'.format('CPU%')
548
        ret.append(self.curse_add_line(msg))
549
        msg = '{:>7}'.format('MEM')
550
        ret.append(self.curse_add_line(msg))
551
        msg = '{:>7}'.format('/MAX')
552
        ret.append(self.curse_add_line(msg))
553
        msg = '{:>7}'.format('IOR/s')
554
        ret.append(self.curse_add_line(msg))
555
        msg = '{:>7}'.format('IOW/s')
556
        ret.append(self.curse_add_line(msg))
557
        msg = '{:>7}'.format('Rx/s')
558
        ret.append(self.curse_add_line(msg))
559
        msg = '{:>7}'.format('Tx/s')
560
        ret.append(self.curse_add_line(msg))
561
        msg = ' {:8}'.format('Command')
562
        ret.append(self.curse_add_line(msg))
563
        # Data
564
        for container in self.stats['containers']:
565
            ret.append(self.curse_new_line())
566
            # Name
567
            ret.append(self.curse_add_line(self._msg_name(container=container,
568
                                                          max_width=name_max_width)))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
569
            # Status
570
            status = self.container_alert(container['Status'])
571
            msg = '{:>10}'.format(container['Status'][0:10])
572
            ret.append(self.curse_add_line(msg, status))
573
            # CPU
574
            try:
575
                msg = '{:>6.1f}'.format(container['cpu']['total'])
576
            except KeyError:
577
                msg = '{:>6}'.format('_')
578
            ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (86/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
579
                                                               key='cpu',
580
                                                               option='decoration')))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
581
            # MEM
582
            try:
583
                msg = '{:>7}'.format(self.auto_unit(container['memory']['usage']))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
584
            except KeyError:
585
                msg = '{:>7}'.format('_')
586
            ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (86/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
587
                                                               key='mem',
588
                                                               option='decoration')))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
589
            try:
590
                msg = '{:>7}'.format(self.auto_unit(container['memory']['limit']))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (82/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
591
            except KeyError:
592
                msg = '{:>7}'.format('_')
593
            ret.append(self.curse_add_line(msg))
594
            # IO R/W
595
            unit = 'B'
596
            for r in ['ior', 'iow']:
0 ignored issues
show
Coding Style Naming introduced by
The name r does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
597
                try:
598
                    value = self.auto_unit(int(container['io'][r] // container['io']['time_since_update'])) + unit
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
599
                    msg = '{:>7}'.format(value)
600
                except KeyError:
601
                    msg = '{:>7}'.format('_')
602
                ret.append(self.curse_add_line(msg))
603
            # NET RX/TX
604
            if args.byte:
605
                # Bytes per second (for dummy)
606
                to_bit = 1
607
                unit = ''
608
            else:
609
                # Bits per second (for real network administrator | Default)
610
                to_bit = 8
611
                unit = 'b'
612
            for r in ['rx', 'tx']:
0 ignored issues
show
Coding Style Naming introduced by
The name r does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
613
                try:
614
                    value = self.auto_unit(int(container['network'][r] // container['network']['time_since_update'] * to_bit)) + unit
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (133/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
615
                    msg = '{:>7}'.format(value)
616
                except KeyError:
617
                    msg = '{:>7}'.format('_')
618
                ret.append(self.curse_add_line(msg))
619
            # Command
620
            if container['Command'] is not None:
621
                msg = ' {}'.format(' '.join(container['Command']))
622
            else:
623
                msg = ' {}'.format('_')
624
            ret.append(self.curse_add_line(msg, splittable=True))
625
626
        return ret
627
628
    def _msg_name(self, container, max_width):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
629
        """Build the container name."""
630
        name = container['name']
631
        if len(name) > max_width:
632
            name = '_' + name[-max_width + 1:]
633
        else:
634
            name = name[:max_width]
635
        return ' {:{width}}'.format(name, width=max_width)
636
637
    def container_alert(self, status):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
638
        """Analyse the container status."""
639
        if status in ('running'):
0 ignored issues
show
Unused Code Coding Style introduced by
Unnecessary parens after u'in' keyword
Loading history...
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
640
            return 'OK'
641
        elif status in ('exited'):
0 ignored issues
show
Unused Code Coding Style introduced by
Unnecessary parens after u'in' keyword
Loading history...
642
            return 'WARNING'
643
        elif status in ('dead'):
0 ignored issues
show
Unused Code Coding Style introduced by
Unnecessary parens after u'in' keyword
Loading history...
644
            return 'CRITICAL'
645
        else:
646
            return 'CAREFUL'
647
648
649
class ThreadDockerGrabber(threading.Thread):
650
    """
651
    Specific thread to grab docker stats.
652
653
    stats is a dict
654
    """
655
656
    def __init__(self, container):
657
        """Init the class.
658
659
        container: instance of Docker-py Container
660
        """
661
        super(ThreadDockerGrabber, self).__init__()
662
        # Event needed to stop properly the thread
663
        self._stopper = threading.Event()
664
        # The docker-py return stats as a stream
665
        self._container = container
666
        self._stats_stream = container.stats(decode=True)
667
        # The class return the stats as a dict
668
        self._stats = {}
669
        logger.debug("docker plugin - Create thread for container {}".format(self._container.name))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (99/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
670
671
    def run(self):
672
        """Grab the stats.
673
674
        Infinite loop, should be stopped by calling the stop() method
675
        """
676
        try:
677
            for i in self._stats_stream:
678
                self._stats = i
679
                time.sleep(0.1)
680
                if self.stopped():
681
                    break
682
        except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
683
            logger.debug("docker plugin - Exception thrown during run")
684
            self.stop()
685
686
    @property
687
    def stats(self):
688
        """Stats getter."""
689
        return self._stats
690
691
    @stats.setter
692
    def stats(self, value):
693
        """Stats setter."""
694
        self._stats = value
695
696
    def stop(self, timeout=None):
0 ignored issues
show
Unused Code introduced by
The argument timeout seems to be unused.
Loading history...
697
        """Stop the thread."""
698
        logger.debug("docker plugin - Close thread for container {}".format(self._container.name))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (98/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
introduced by
Use formatting in logging functions and pass the parameters as arguments
Loading history...
699
        self._stopper.set()
700
701
    def stopped(self):
702
        """Return True is the thread is stopped."""
703
        return self._stopper.isSet()
704
705
706
def sort_stats(stats):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
707
    # Sort Docker stats using the same function than processes
708
    sortedby = 'cpu_percent'
709
    sortedby_secondary = 'memory_usage'
710
    if glances_processes.sort_key.startswith('memory'):
711
        sortedby = 'memory_usage'
712
        sortedby_secondary = 'cpu_percent'
713
    sort_stats_processes(stats['containers'],
714
                         sortedby=sortedby,
715
                         sortedby_secondary=sortedby_secondary)
716
    return stats
717