From 879ec5eb58cc223295770e5570bc50d996b06d75 Mon Sep 17 00:00:00 2001 From: Nicolargo Date: Fri, 2 May 2014 10:00:10 +0200 Subject: [PATCH] Structure and file for the SNMP client --- glances/core/glances_client.py | 110 ++++++++++++++++++++++++++++++--- glances/core/glances_snmp.py | 57 +++++++++++++++++ glances/core/glances_stats.py | 38 +++++++++++- 3 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 glances/core/glances_snmp.py diff --git a/glances/core/glances_client.py b/glances/core/glances_client.py index f0f8a4ad..7eeed47d 100644 --- a/glances/core/glances_client.py +++ b/glances/core/glances_client.py @@ -45,6 +45,9 @@ class GlancesClient(): self.args = args self.config = config + # Client mode: + self.set_mode() + # Build the URI if args.password != "": uri = 'http://%s:%s@%s:%d' % (args.username, args.password, args.bind_address, args.port) @@ -58,24 +61,46 @@ class GlancesClient(): print(_("Error: Couldn't create socket {0}: {1}").format(uri, err)) sys.exit(2) + def set_mode(self, mode='glances'): + """ + Set the client mode + - 'glances' = Glances server (default) + - 'snmp' = SNMP (fallback) + """ + self.mode = mode + return self.mode + + def get_mode(self): + """ + Return the client mode + - 'glances' = Glances server (default) + - 'snmp' = SNMP (fallback) + """ + return self.mode + def login(self): """ Logon to the server """ + + ret = True + + # First of all, trying to connect to a Glances server try: client_version = self.client.init() except socket.error as err: - print(_("Error: Connection to server failed: {0}").format(err)) - sys.exit(2) + print(_("Error: Connection to {0} server failed").format(self.get_mode())) + # Fallback to SNMP + self.set_mode('snmp') except ProtocolError as err: + # Others errors if str(err).find(" 401 ") > 0: print(_("Error: Connection to server failed: Bad password")) else: print(_("Error: Connection to server failed: {0}").format(err)) sys.exit(2) - # Test if client and server are "compatible" - if __version__[:3] == client_version[:3]: + if self.get_mode() == 'glances' and __version__[:3] == client_version[:3]: # Init stats self.stats = GlancesStatsClient() self.stats.set_plugins(json.loads(self.client.getAllPlugins())) @@ -86,12 +111,43 @@ class GlancesClient(): # Init screen self.screen = glancesCurses(args=self.args) - - # Debug - # print "Server version: {0}\nClient version: {1}\n".format(__version__, client_version) - return True + + ret = True else: - return False + ret = False + + # Then fallback to SNMP if needed + if self.get_mode() == 'snmp': + from glances.core.glances_snmp import GlancesSNMPClient + + # Test if SNMP is available on the server side + clientsnmp = GlancesSNMPClient() + try: + # !!! Simple request with system name + # !!! Had to have a standard method to check SNMP server + clientsnmp.get_by_oid("1.3.6.1.2.1.1.5.0") + except: + print(_("Error: Connection to {0} server failed").format(self.get_mode())) + sys.exit(2) + + from glances.core.glances_stats import GlancesStatsClientSNMP + + print(_("Fallback to {0}").format(self.get_mode())) + + # Init stats + self.stats = GlancesStatsClientSNMP() + + # Load limits from the configuration file + # Each client can choose its owns limits + self.stats.load_limits(self.config) + + # Init screen + self.screen = glancesCurses(args=self.args) + + return True + + # Return result + return ret def update(self): """ @@ -101,6 +157,22 @@ class GlancesClient(): - Disconnected: Connection NOK """ # Update the stats + if self.get_mode() == 'glances': + return self.update_glances() + elif self.get_mode() == 'snmp': + return self.update_snmp() + else: + print(_("Error: Unknown server mode ({0})").format(self.get_mode())) + sys.exit(2) + + def update_glances(self): + """ + Get stats from Glances server + Return the client/server connection status: + - Connected: Connection OK + - Disconnected: Connection NOK + """ + # Update the stats try: server_stats = json.loads(self.client.getAll()) server_stats['monitor'] = json.loads(self.client.getAllMonitored()) @@ -112,6 +184,23 @@ class GlancesClient(): self.stats.update(server_stats) return "Connected" + def update_snmp(self): + """ + Get stats from SNMP server + Return the client/server connection status: + - Connected: Connection OK + - Disconnected: Connection NOK + """ + # Update the stats + try: + self.stats.update() + except socket.error as e: + # Client can not get SNMP server stats + return "Disconnected" + else: + # Grab success + return "Connected" + def serve_forever(self): """ Main client loop @@ -122,9 +211,12 @@ class GlancesClient(): # Update the screen self.screen.update(self.stats, cs_status=cs_status) + # print self.stats + # print self.stats.getAll() def end(self): """ End of the client session """ self.screen.end() + \ No newline at end of file diff --git a/glances/core/glances_snmp.py b/glances/core/glances_snmp.py new file mode 100644 index 00000000..c4902a2c --- /dev/null +++ b/glances/core/glances_snmp.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Glances. +# +# Copyright (C) 2014 Nicolargo +# +# 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 . + +import sys + +try: + from pysnmp.entity.rfc3413.oneliner import cmdgen +except ImportError, e: + print("Error importing PySNMP lib: %s" % e) + print("Install using pip: # pip install pysnmp") + sys.exit(2) + + +class GlancesSNMPClient(object): + """ SNMP client class (based on PySNMP) """ + + def __init__(self, host = "localhost", + port = 161, + community = "public", + version = "SNMPv2-MIB"): + super(GlancesSNMPClient, self).__init__() + self.cmdGen = cmdgen.CommandGenerator() + self.host = host + self.port = port + self.community = community + self.version = version + + def __result__(self, errorIndication, errorStatus, errorIndex, varBinds): + ret = {} + if not (errorIndication or errorStatus): + for name, val in varBinds: + ret[name.prettyPrint()] = val.prettyPrint() + return ret + + def get_by_oid(self, *oid): + errorIndication, errorStatus, errorIndex, varBinds = self.cmdGen.getCmd( + cmdgen.CommunityData(self.community), + cmdgen.UdpTransportTarget((self.host, self.port)), + *oid + ) + return self.__result__(errorIndication, errorStatus, errorIndex, varBinds) diff --git a/glances/core/glances_stats.py b/glances/core/glances_stats.py index 4ebf1545..c9d779f6 100644 --- a/glances/core/glances_stats.py +++ b/glances/core/glances_stats.py @@ -87,6 +87,12 @@ class GlancesStats(object): plugname = os.path.basename(plug)[len(header):-3].lower() self._plugins[plugname] = m.Plugin() + def getAllPlugins(self): + """ + Return the plugins list + """ + return [p for p in self._plugins] + def load_limits(self, config=None): """ Load the stats limits @@ -109,7 +115,7 @@ class GlancesStats(object): # print "DEBUG: Update %s stats" % p self._plugins[p].update() else: - # For client mode + # For Glances client mode # Update plugin stats with items sent by the server for p in input_stats: self._plugins[p].set_stats(input_stats[p]) @@ -118,6 +124,12 @@ class GlancesStats(object): # Update the stats self.__update__(input_stats) + def getAll(self): + """ + Return all the stats + """ + return [ self._plugins[p].get_raw() for p in self._plugins ] + def get_plugin_list(self): # Return the plugin list self._plugins @@ -202,3 +214,27 @@ class GlancesStatsClient(GlancesStats): # generate self._plugins_list["xxx"] = ... # print "DEBUG: Init %s plugin" % plug self._plugins[plug] = m.Plugin() + + +class GlancesStatsClientSNMP(GlancesStats): + """ + This class store, update and give stats for the SNMP client + """ + + def __init__(self): + # Init the plugin list dict + self._plugins = collections.defaultdict(dict) + + # Load plugins + self.load_plugins() + + def update(self): + """ + Update the stats using SNMP + """ + + # For each plugins, call the update method + for p in self._plugins: + # print "DEBUG: Update %s stats using SNMP request" % p + self._plugins[p].update() +