Completed
Push — master ( bcff18...adb900 )
by Nicolas
01:15
created

glances.GlancesClient.log_and_exit()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 7
rs 9.4285
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2015 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Manage the Glances client."""
21
22
import json
23
import socket
24
import sys
25
26
from glances.compat import Fault, ProtocolError, ServerProxy, Transport
27
from glances.globals import version
28
from glances.logger import logger
29
from glances.stats import GlancesStatsClient
30
from glances.outputs.glances_curses import GlancesCursesClient
31
32
33
class GlancesClientTransport(Transport):
34
35
    """This class overwrite the default XML-RPC transport and manage timeout."""
36
37
    def set_timeout(self, timeout):
38
        self.timeout = timeout
39
40
41
class GlancesClient(object):
42
43
    """This class creates and manages the TCP client."""
44
45
    def __init__(self, config=None, args=None, timeout=7, return_to_browser=False):
46
        # Store the arg/config
47
        self.args = args
48
        self.config = config
49
50
        # Default client mode
51
        self._client_mode = 'glances'
52
53
        # Return to browser or exit
54
        self.return_to_browser = return_to_browser
55
56
        # Build the URI
57
        if args.password != "":
58
            uri = 'http://{0}:{1}@{2}:{3}'.format(args.username, args.password,
59
                                                  args.client, args.port)
60
        else:
61
            uri = 'http://{0}:{1}'.format(args.client, args.port)
62
        logger.debug("Try to connect to {0}".format(uri))
63
64
        # Try to connect to the URI
65
        transport = GlancesClientTransport()
66
        # Configure the server timeout
67
        transport.set_timeout(timeout)
68
        try:
69
            self.client = ServerProxy(uri, transport=transport)
70
        except Exception as e:
71
            self.log_and_exit("Client couldn't create socket {0}: {1}".format(uri, e))
72
73
    def log_and_exit(self, msg=''):
74
        """Log and exit."""
75
        if not self.return_to_browser:
76
            logger.critical(msg)
77
            sys.exit(2)
78
        else:
79
            logger.error(msg)
80
81
    @property
82
    def client_mode(self):
83
        """Get the client mode."""
84
        return self._client_mode
85
86
    @client_mode.setter
87
    def client_mode(self, mode):
88
        """Set the client mode.
89
90
        - 'glances' = Glances server (default)
91
        - 'snmp' = SNMP (fallback)
92
        """
93
        self._client_mode = mode
94
95
    def login(self):
96
        """Logon to the server."""
97
        ret = True
98
99
        if not self.args.snmp_force:
100
            # First of all, trying to connect to a Glances server
101
            client_version = None
102
            try:
103
                client_version = self.client.init()
104
            except socket.error as err:
105
                # Fallback to SNMP
106
                self.client_mode = 'snmp'
107
                logger.error("Connection to Glances server failed ({0} {1})".format(err.errno, err.strerror))
108
                fallbackmsg = 'No Glances server found. Trying fallback to SNMP...'
109
                if not self.return_to_browser:
110
                    print(fallbackmsg)
111
                else:
112
                    logger.info(fallbackmsg)
113
            except ProtocolError as err:
114
                # Other errors
115
                msg = "Connection to server failed"
116
                if err.errcode == 401:
117
                    msg += " (Bad username/password)"
118
                else:
119
                    msg += " ({0} {1})".format(err.errcode, err.errmsg)
120
                self.log_and_exit(msg)
121
                return False
122
123
            if self.client_mode == 'glances':
124
                # Check that both client and server are in the same major version
125
                if version.split('.')[0] == client_version.split('.')[0]:
126
                    # Init stats
127
                    self.stats = GlancesStatsClient(config=self.config, args=self.args)
128
                    self.stats.set_plugins(json.loads(self.client.getAllPlugins()))
129
                    logger.debug("Client version: {0} / Server version: {1}".format(version, client_version))
130
                else:
131
                    self.log_and_exit("Client and server not compatible: \
132
                                      Client version: {0} / Server version: {1}".format(version, client_version))
133
                    return False
134
135
        else:
136
            self.client_mode = 'snmp'
137
138
        # SNMP mode
139
        if self.client_mode == 'snmp':
140
            logger.info("Trying to grab stats by SNMP...")
141
142
            from glances.stats import GlancesStatsClientSNMP
143
144
            # Init stats
145
            self.stats = GlancesStatsClientSNMP(config=self.config, args=self.args)
146
147
            if not self.stats.check_snmp():
148
                self.log_and_exit("Connection to SNMP server failed")
149
                return False
150
151
        if ret:
152
            # Load limits from the configuration file
153
            # Each client can choose its owns limits
154
            self.stats.load_limits(self.config)
155
156
            # Init screen
157
            self.screen = GlancesCursesClient(args=self.args)
158
159
        # Return result
160
        return ret
161
162
    def update(self):
163
        """Update stats from Glances/SNMP server."""
164
        if self.client_mode == 'glances':
165
            return self.update_glances()
166
        elif self.client_mode == 'snmp':
167
            return self.update_snmp()
168
        else:
169
            self.end()
170
            logger.critical("Unknown server mode: {0}".format(self.client_mode))
171
            sys.exit(2)
172
173
    def update_glances(self):
174
        """Get stats from Glances server.
175
176
        Return the client/server connection status:
177
        - Connected: Connection OK
178
        - Disconnected: Connection NOK
179
        """
180
        # Update the stats
181
        try:
182
            server_stats = json.loads(self.client.getAll())
183
            server_stats['monitor'] = json.loads(self.client.getAllMonitored())
184
        except socket.error:
185
            # Client cannot get server stats
186
            return "Disconnected"
187
        except Fault:
188
            # Client cannot get server stats (issue #375)
189
            return "Disconnected"
190
        else:
191
            # Put it in the internal dict
192
            self.stats.update(server_stats)
193
            return "Connected"
194
195
    def update_snmp(self):
196
        """Get stats from SNMP server.
197
198
        Return the client/server connection status:
199
        - SNMP: Connection with SNMP server OK
200
        - Disconnected: Connection NOK
201
        """
202
        # Update the stats
203
        try:
204
            self.stats.update()
205
        except Exception:
206
            # Client cannot get SNMP server stats
207
            return "Disconnected"
208
        else:
209
            # Grab success
210
            return "SNMP"
211
212
    def serve_forever(self):
213
        """Main client loop."""
214
        exitkey = False
215
        try:
216
            while True and not exitkey:
217
                # Update the stats
218
                cs_status = self.update()
219
220
                # Update the screen
221
                exitkey = self.screen.update(self.stats,
222
                                             cs_status=cs_status,
223
                                             return_to_browser=self.return_to_browser)
224
225
                # Export stats using export modules
226
                self.stats.export(self.stats)
227
        except Exception as e:
228
            logger.critical(e)
229
            self.end()
230
231
        return self.client_mode
232
233
    def end(self):
234
        """End of the client session."""
235
        self.screen.end()
236