glances_autodiscover.py 8.3 KB
Newer Older
N
Nicolargo 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
#
# 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 <http://www.gnu.org/licenses/>.

"""Manage autodiscover Glances server (thk to the ZeroConf protocol)."""

# Import system libs
23
import sys
N
Nicolargo 已提交
24
import socket
N
Nicolargo 已提交
25 26 27 28 29
try:
    import netifaces
    netifaces_tag = True
except ImportError:
    netifaces_tag = True
N
Nicolargo 已提交
30 31 32 33 34 35 36
try:
    from zeroconf import ServiceBrowser, ServiceInfo, Zeroconf
    zeroconf_tag = True
except ImportError:
    zeroconf_tag = False

# Import Glances libs
N
Nicolargo 已提交
37
from glances.core.glances_globals import appname, logger
N
Nicolargo 已提交
38 39 40 41 42 43

# Global var
zeroconf_type = "_%s._tcp." % appname


class AutoDiscovered(object):
N
Nicolargo 已提交
44

N
Nicolargo 已提交
45 46 47
    """Class to manage the auto discovered servers dict"""

    def __init__(self):
48 49 50
        # server_dict is a list of dict (JSON compliant)
        # [ {'key': 'zeroconf name', ip': '172.1.2.3', 'port': 61209, 'cpu': 3, 'mem': 34 ...} ... ]
        self._server_list = []
N
Nicolargo 已提交
51 52

    def get_servers_list(self):
53 54
        """Return the current server list (list of dict)"""
        return self._server_list
N
Nicolargo 已提交
55

56 57 58 59
    def set_server(self, server_pos, key, value):
        """Set the key to the value for the server_pos (position in the list)"""
        self._server_list[server_pos][key] = value

N
Nicolargo 已提交
60
    def add_server(self, name, ip, port):
61
        """Add a new server to the list"""
N
Nicolargo 已提交
62 63 64 65
        new_server = {'key': name,  # Zeroconf name with both hostname and port
                      'name': name.split(':')[0],  # Short name
                      'ip': ip,  # IP address seen by the client
                      'port': port,  # TCP port
66 67
                      'username': 'glances', # Default username
                      'password': '', # Default password
N
Nicolargo 已提交
68
                      'status': 'UNKNOWN', # Server status: 'UNKNOWN', 'OFFLINE', 'ONLINE', 'PROTECTED'
N
Nicolargo 已提交
69
                      }
70
        self._server_list.append(new_server)
N
Nicolargo 已提交
71 72
        logger.debug("Updated servers list (%s servers): %s" %
                     (len(self._server_list), self._server_list))
N
Nicolargo 已提交
73 74 75

    def remove_server(self, name):
        """Remove a server from the dict"""
76 77 78 79 80
        for i in self._server_list:
            if i['key'] == name:
                try:
                    self._server_list.remove(i)
                    logger.debug("Remove server %s from the list" % name)
N
Nicolargo 已提交
81 82
                    logger.debug("Updated servers list (%s servers): %s" % (
                        len(self._server_list), self._server_list))
83
                except ValueError:
N
Nicolargo 已提交
84 85
                    logger.error(
                        "Can not remove server %s from the list" % name)
N
Nicolargo 已提交
86 87 88


class GlancesAutoDiscoverListener(object):
N
Nicolargo 已提交
89

N
Nicolargo 已提交
90 91 92 93 94 95 96
    """Zeroconf listener for Glances server"""

    def __init__(self):
        # Create an instance of the servers list
        self.servers = AutoDiscovered()

    def get_servers_list(self):
97
        """Return the current server list (list of dict)"""
N
Nicolargo 已提交
98 99
        return self.servers.get_servers_list()

100 101 102 103
    def set_server(self, server_pos, key, value):
        """Set the key to the value for the server_pos (position in the list)"""
        self.servers.set_server(server_pos, key, value)

N
Nicolargo 已提交
104 105 106 107 108 109 110
    def addService(self, zeroconf, srv_type, srv_name):
        """Method called when a new Zeroconf client is detected
        Return True if the zeroconf client is a Glances server
        Note: the return code will never be used
        """
        if srv_type != zeroconf_type:
            return False
N
Nicolargo 已提交
111 112
        logger.debug("Check new Zeroconf server: %s / %s" %
                     (srv_type, srv_name))
N
Nicolargo 已提交
113
        info = zeroconf.getServiceInfo(srv_type, srv_name)
N
Nicolargo 已提交
114 115 116 117 118 119
        if info:
            new_server_ip = socket.inet_ntoa(info.getAddress())
            new_server_port = info.getPort()

            # Add server to the global dict
            self.servers.add_server(srv_name, new_server_ip, new_server_port)
N
Nicolargo 已提交
120 121
            logger.info("New Glances server detected (%s from %s:%s)" %
                        (srv_name, new_server_ip, new_server_port))
N
Nicolargo 已提交
122
        else:
N
Nicolargo 已提交
123 124
            logger.warning(
                "New Glances server detected, but Zeroconf info failed to be grabbed")
N
Nicolargo 已提交
125 126 127 128 129
        return True

    def removeService(self, zeroconf, srv_type, srv_name):
        # Remove the server from the list
        self.servers.remove_server(srv_name)
N
Nicolargo 已提交
130 131
        logger.info(
            "Glances server %s removed from the autodetect list" % srv_name)
N
Nicolargo 已提交
132 133 134


class GlancesAutoDiscoverServer(object):
N
Nicolargo 已提交
135

N
Nicolargo 已提交
136 137 138 139 140
    """Implementation of the Zeroconf protocol (server side for the Glances client)"""

    def __init__(self, args=None):
        if zeroconf_tag:
            logger.info("Init autodiscover mode (Zeroconf protocol)")
141 142
            try:
                self.zeroconf = Zeroconf()
143
            except socket.error as e:
144 145
                logger.error("Can not start Zeroconf (%s)" % e)
                self.zeroconf_enable_tag = False
146
            else:
147 148 149 150
                self.listener = GlancesAutoDiscoverListener()
                self.browser = ServiceBrowser(
                    self.zeroconf, zeroconf_type, self.listener)
                self.zeroconf_enable_tag = True
N
Nicolargo 已提交
151
        else:
152
            logger.error(
N
Nicolargo 已提交
153
                "Can not start autodiscover mode (Zeroconf lib is not installed)")
154
            self.zeroconf_enable_tag = False
N
Nicolargo 已提交
155 156 157

    def get_servers_list(self):
        """Return the current server list (dict of dict)"""
158
        if zeroconf_tag and self.zeroconf_enable_tag:
N
Nicolargo 已提交
159 160
            return self.listener.get_servers_list()
        else:
N
Nicolargo 已提交
161
            return []
N
Nicolargo 已提交
162

163 164
    def set_server(self, server_pos, key, value):
        """Set the key to the value for the server_pos (position in the list)"""
165 166
        if zeroconf_tag and self.zeroconf_enable_tag:
            self.listener.set_server(server_pos, key, value)
167

N
Nicolargo 已提交
168
    def close(self):
169
        if zeroconf_tag and self.zeroconf_enable_tag:
N
Nicolargo 已提交
170 171 172 173
            self.zeroconf.close()


class GlancesAutoDiscoverClient(object):
N
Nicolargo 已提交
174

N
Nicolargo 已提交
175 176 177
    """Implementation of the Zeroconf protocol (client side for the Glances server)"""

    def __init__(self, hostname, args=None):
N
Nicolargo 已提交
178 179
        if netifaces_tag:
            try:
N
Nicolargo 已提交
180 181
                zeroconf_bind_address = netifaces.ifaddresses(
                    netifaces.interfaces()[1])[netifaces.AF_INET][0]['addr']
N
Nicolargo 已提交
182
            except:
N
Nicolargo 已提交
183
                zeroconf_bind_address = args.bind_address
N
Nicolargo 已提交
184 185
            print("Announce the Glances server on the local area network (using %s IP address)" %
                  zeroconf_bind_address)
N
Nicolargo 已提交
186 187 188 189

            if zeroconf_tag:
                logger.info(
                    "Announce the Glances server on the local area network (using %s IP address)" % zeroconf_bind_address)
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
                try:
                    self.zeroconf = Zeroconf()
                except socket.error as e:
                    logger.error("Can not start Zeroconf (%s)" % e)
                else:
                    self.info = ServiceInfo(zeroconf_type,
                                            hostname + ':' +
                                            str(args.port) + '.' + zeroconf_type,
                                            address=socket.inet_aton(
                                                zeroconf_bind_address),
                                            port=args.port,
                                            weight=0,
                                            priority=0,
                                            properties={},
                                            server=hostname)
                    self.zeroconf.registerService(self.info)
N
Nicolargo 已提交
206 207 208
            else:
                logger.error(
                    "Can not announce Glances server on the network (Zeroconf lib is not installed)")
N
Nicolargo 已提交
209
        else:
N
Nicolargo 已提交
210 211
            logger.error(
                "Can not announce Glances server on the network (Netifaces lib is not installed)")
N
Nicolargo 已提交
212 213 214 215 216

    def close(self):
        if zeroconf_tag:
            self.zeroconf.unregisterService(self.info)
            self.zeroconf.close()