PageRenderTime 35ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/glances/core/glances_client_browser.py

https://gitlab.com/xbsd/glances
Python | 226 lines | 173 code | 15 blank | 38 comment | 12 complexity | cf994b0eef028f8b14269a79ec83f1d6 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. #
  3. # This file is part of Glances.
  4. #
  5. # Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
  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. """Manage the Glances client browser (list of Glances server)."""
  20. # Import system libs
  21. import json
  22. import socket
  23. try:
  24. from xmlrpc.client import ServerProxy, Fault, ProtocolError
  25. except ImportError:
  26. # Python 2
  27. from xmlrpclib import ServerProxy, Fault, ProtocolError
  28. # Import Glances libs
  29. from glances.core.glances_globals import logger
  30. from glances.outputs.glances_curses import GlancesCursesBrowser
  31. from glances.core.glances_autodiscover import GlancesAutoDiscoverServer
  32. from glances.core.glances_client import GlancesClient, GlancesClientTransport
  33. from glances.core.glances_staticlist import GlancesStaticServer
  34. class GlancesClientBrowser(object):
  35. """This class creates and manages the TCP client browser (servers' list)."""
  36. def __init__(self, config=None, args=None):
  37. # Store the arg/config
  38. self.args = args
  39. self.config = config
  40. # Init the static server list (if defined)
  41. self.static_server = GlancesStaticServer(config=self.config)
  42. # Start the autodiscover mode (Zeroconf listener)
  43. if not self.args.disable_autodiscover:
  44. self.autodiscover_server = GlancesAutoDiscoverServer()
  45. else:
  46. self.autodiscover_server = None
  47. # Init screen
  48. self.screen = GlancesCursesBrowser(args=self.args)
  49. def get_servers_list(self):
  50. """
  51. Return the current server list (list of dict)
  52. Merge of static + autodiscover servers list
  53. """
  54. ret = []
  55. if self.args.browser:
  56. ret = self.static_server.get_servers_list()
  57. if self.autodiscover_server is not None:
  58. ret = self.static_server.get_servers_list() + self.autodiscover_server.get_servers_list()
  59. return ret
  60. def serve_forever(self):
  61. """Main client loop."""
  62. while True:
  63. # No need to update the server list
  64. # It's done by the GlancesAutoDiscoverListener class (glances_autodiscover.py)
  65. # Or define staticaly in the configuration file (module glances_staticlist.py)
  66. # For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...)
  67. # logger.debug(self.get_servers_list())
  68. try:
  69. for v in self.get_servers_list():
  70. # Do not retreive stats for statics server
  71. # Why ? Because for each offline servers, the timeout will be reached
  72. # So ? The curse interface freezes
  73. if (v['type'] == 'STATIC' and v['status'] in ['UNKNOWN', 'SNMP', 'OFFLINE']):
  74. continue
  75. # Select the connection mode (with or without password)
  76. if v['password'] != "":
  77. uri = 'http://{0}:{1}@{2}:{3}'.format(v['username'], v['password'],
  78. v['ip'], v['port'])
  79. else:
  80. uri = 'http://{0}:{1}'.format(v['ip'], v['port'])
  81. # Try to connect to the server
  82. t = GlancesClientTransport()
  83. t.set_timeout(3)
  84. # Get common stats
  85. try:
  86. s = ServerProxy(uri, transport=t)
  87. except Exception as e:
  88. logger.warning(
  89. "Client browser couldn't create socket {0}: {1}".format(uri, e))
  90. else:
  91. # Mandatory stats
  92. try:
  93. # CPU%
  94. cpu_percent = 100 - json.loads(s.getCpu())['idle']
  95. v['cpu_percent'] = '{0:.1f}'.format(cpu_percent)
  96. # MEM%
  97. v['mem_percent'] = json.loads(s.getMem())['percent']
  98. # OS (Human Readable name)
  99. v['hr_name'] = json.loads(s.getSystem())['hr_name']
  100. except (socket.error, Fault, KeyError) as e:
  101. logger.debug(
  102. "Error while grabbing stats form {0}: {1}".format(uri, e))
  103. v['status'] = 'OFFLINE'
  104. except ProtocolError as e:
  105. if str(e).find(" 401 ") > 0:
  106. # Error 401 (Authentication failed)
  107. # Password is not the good one...
  108. v['password'] = None
  109. v['status'] = 'PROTECTED'
  110. else:
  111. v['status'] = 'OFFLINE'
  112. logger.debug(
  113. "Cannot grab stats from {0}: {1}".format(uri, e))
  114. else:
  115. # Status
  116. v['status'] = 'ONLINE'
  117. # Optional stats (load is not available on Windows OS)
  118. try:
  119. # LOAD
  120. load_min5 = json.loads(s.getLoad())['min5']
  121. v['load_min5'] = '{0:.2f}'.format(load_min5)
  122. except Exception as e:
  123. logger.warning(
  124. "Error while grabbing stats form {0}: {1}".format(uri, e))
  125. # List can change size during iteration...
  126. except RuntimeError:
  127. logger.debug(
  128. "Server list dictionnary change inside the loop (wait next update)")
  129. # Update the screen (list or Glances client)
  130. if self.screen.get_active() is None:
  131. # Display the Glances browser
  132. self.screen.update(self.get_servers_list())
  133. else:
  134. # Display the Glances client for the selected server
  135. logger.debug("Selected server: %s" % self.get_servers_list()[self.screen.get_active()])
  136. # Connection can take time
  137. # Display a popup
  138. self.screen.display_popup(_("Connect to %s:%s" % (v['name'], v['port'])), duration=1)
  139. # A password is needed to access to the server's stats
  140. if self.get_servers_list()[self.screen.get_active()]['password'] is None:
  141. from hashlib import sha256
  142. # Display a popup to enter password
  143. clear_password = self.screen.display_popup(_("Password needed for %s: " % v['name']), is_input=True)
  144. # Hash with SHA256
  145. encoded_password = sha256(clear_password).hexdigest()
  146. # Store the password for the selected server
  147. self.set_in_selected('password', encoded_password)
  148. # Display the Glance client on the selected server
  149. logger.info("Connect Glances client to the %s server" %
  150. self.get_servers_list()[self.screen.get_active()]['key'])
  151. # Init the client
  152. args_server = self.args
  153. # Overwrite connection setting
  154. args_server.client = self.get_servers_list()[self.screen.get_active()]['ip']
  155. args_server.port = self.get_servers_list()[self.screen.get_active()]['port']
  156. args_server.username = self.get_servers_list()[self.screen.get_active()]['username']
  157. args_server.password = self.get_servers_list()[self.screen.get_active()]['password']
  158. client = GlancesClient(config=self.config,
  159. args=args_server)
  160. # Test if client and server are in the same major version
  161. if not client.login(return_to_browser=True):
  162. self.screen.display_popup(_("Sorry, cannot connect to %s (see log file for additional information)" % v['name']))
  163. # Set the ONLINE status for the selected server
  164. self.set_in_selected('status', 'OFFLINE')
  165. else:
  166. # Start the client loop
  167. # Return connection type: 'glances' or 'snmp'
  168. connection_type = client.serve_forever(return_to_browser=True)
  169. try:
  170. logger.debug("Disconnect Glances client from the %s server" %
  171. self.get_servers_list()[self.screen.get_active()]['key'])
  172. except IndexError:
  173. # Server did not exist anymore
  174. pass
  175. else:
  176. # Set the ONLINE status for the selected server
  177. if connection_type == 'snmp':
  178. self.set_in_selected('status', 'SNMP')
  179. else:
  180. self.set_in_selected('status', 'ONLINE')
  181. # Return to the browser (no server selected)
  182. self.screen.set_active(None)
  183. def set_in_selected(self, key, value):
  184. """Set the (key, value) for the selected server in the list"""
  185. # Static list then dynamic one
  186. if self.screen.get_active() >= len(self.static_server.get_servers_list()):
  187. self.autodiscover_server.set_server(self.screen.get_active() - len(self.static_server.get_servers_list()),
  188. key,
  189. value)
  190. else:
  191. self.static_server.set_server(self.screen.get_active(),
  192. key,
  193. value)
  194. def end(self):
  195. """End of the client browser session."""
  196. self.screen.end()